const express = require("express");
const router = express.Router();
const path = require("path");
const utils = require("../utils/index.js");
const moment = require("moment-timezone");
const multer = require("multer");
const AdmZip = require("adm-zip");
const AppInfoParser = require("app-info-parser");
const plist = require("plist");
const fs = require("fs");
const msgUtils = require("../utils/feishu.js");
const crypto = require("crypto");
const CryptoJS = require("crypto-js");
const FormData = require("form-data");
const {
    exec
} = require("child_process");
const config = require("../config");
const axios = require("axios");
const jwt = require("jsonwebtoken");

const poolTarot = require("../poolTarot.js");
const pool = require("../pool.js");
const mapping = {
    "财富": "wealth",
    "艰难时刻": "tough_times",
    "人际交往": "social_interaction",
    "美丽与形象": "beauty_and_image",
    "自我关怀": "self_care",
    "关注健康": "health_focus",
    "浪漫关系": "romance",
    "自我成长": "self_growth",
    "事业与发展": "career_and_growth",
    "治愈之旅": "healing_journey",
    "感恩与欣赏": "gratitude",
    "放松与解压": "relaxation",
    "综合": "comprehensive"
};

const moonPhaseMapping = {
    "新月": "new_moon",
    "峨眉月": "waxing_crescent",
    "上弦月": "first_quarter",
    "盈凸月": "waxing_gibbous",
    "满月": "full_moon",
    "亏凸月": "waning_gibbous",
    "下弦月": "last_quarter",
    "残月": "waning_crescent"
};
// 配置 multer 的存储设置
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        // 设置文件上传目录
        cb(null, "/home/wwwroot/default/TarotImages");
    },
    filename: function (req, file, cb) {
        // 生成文件名
        cb(null, Date.now() + "-" + file.originalname);
    },
});

const upload = multer({ storage: storage });

// 创建文件上传的 POST 接口
router.post("/upload", upload.single("file"), (req, res) => {
    const file = req.file;
    if (!file) {
        return res.status(400).send("请上传一个文件。");
    }
    let fileUrl = ''
    fileUrl = `https://xiaoshuchengzhang.com/TarotImages/${file.filename}`;
    // 返回文件存储地址
    res.send({
        code: 0,
        message: "success",
        fileUrl: fileUrl,
    });
});
router.post('/test', async (req, res) => {
    const sql = 'SELECT * FROM transir_users';
    const result = await mysqlQueryTarot(sql, []);
    res.send({ data: result })
})


/**
 * 苹果登录接口 
 * POST /api/login/apple
 * body: { identityToken: string }             
 */
router.post('/login', async (req, res) => {
    try {
        const {
            identifier,
            gender,
            interests,
            country,
            city
        } = req.body;
        if (!identifier  ) {
            return res.status(400).json({ code: 400, message: "缺少参数" });
        }
        let cleanInterests = null
        if (interests) {
             cleanInterests = interests
                .split(',')
                .map(e => e.trim())
                .filter(Boolean)
                .join(',');
        }
        
        const { user, isNew } = await checkOrCreateUserByType(identifier, 'apple', gender, cleanInterests, country, city);
        res.send({
            code: 0,
            message: 'success',
            data: {
                user,
                isNew
            }
        });
    } catch (e) {
        console.error(`登录异常:`, e);
        res.status(500).json({ code: 500, message: '服务器异常' });
    }
});
// 工具函数-获取北京时间
function getNowInBeijing() {
    return moment().tz("Asia/Shanghai").format("YYYY-MM-DD HH:mm:ss");
}
// 随机邀请码生成
async function generateInviteCode() {
    // 可以用随机数字字母或第三方生成逻辑
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let code = '';
    for (let i = 0; i < 6; i++) code += chars[Math.floor(Math.random() * chars.length)];
    // 确认不重复
    let retry = 0;
    let exists = true;
    while (exists && retry < 5) {
        const rows = await mysqlQueryTarot('SELECT 1 FROM tarot_users WHERE invitation_code = ?', [code]);
        exists = rows.length > 0;
        if (exists) code = chars.split('').sort(() => 0.5 - Math.random()).slice(0, 6).join('');
        retry++;
    }
    return code;
}
// 生成用户名
function generateDefaultUsername() {
    // 6位字母数字
    const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
    let randStr = '';
    for (let i = 0; i < 6; i++) randStr += chars[Math.floor(Math.random() * chars.length)];
    return `用户-${randStr}`;
}
async function checkOrCreateUserByType(identifier, type, gender, interests, country, city) {
    let fieldName = type === 'apple' ? 'apple_identifier' : 'email_identifier';
    let querySql = `SELECT * FROM tarot_users WHERE ${fieldName} = ? LIMIT 1`;
    let result = await mysqlQueryTarot(querySql, [identifier]);

    if (result.length > 0) {
        // 老用户，刷新登录时间
        const updateSql = 'UPDATE tarot_users SET last_login_time = NOW() WHERE user_id = ?';
        await mysqlQueryTarot(updateSql, [result[0].user_id]);
        let user = result[0];
        // 格式化时间
        [
            'registration_time',
            'last_login_time',
            'created_at',
            'updated_at',
            'subscription_start_date',
            'subscription_expiry_date',
            'invitation_time'
        ].forEach(field => {
            user[field] = user[field]
                ? moment(user[field])
                    .tz("Asia/Shanghai")
                    .format("YYYY-MM-DD HH:mm:ss")
                : null;
        });
        return { user, isNew: false };
    } else {
        // 新用户
        const userId = utils.createId();
        const username = generateDefaultUsername();
        const avatarList = [
            'icon1', 'icon2', 'icon3', 'icon4', 'icon5', 'icon6'
        ];
        const avatarUrl = avatarList[Math.floor(Math.random() * avatarList.length)];
        const now = getNowInBeijing();
        const inviteCode = await generateInviteCode();

        let fields = [
            'apple_identifier', 'user_id', 'auth_type', 'username', 'avatar_url',
            'registration_time', 'last_login_time',
            'created_at', 'updated_at', 'invitation_code', 'gender', 'interests', 'country', 'city'
        ];
        let values = [
            identifier, userId, type, username, avatarUrl,
            now, now, now, now, inviteCode, gender, interests, country, city
        ];





        let insertSql = `INSERT INTO tarot_users (${fields.join(',')}) VALUES (${fields.map(() => "?").join(',')})`;

        await mysqlQueryTarot(insertSql, values);

        // 新用户从库里查出
        let rows = await mysqlQueryTarot('SELECT * FROM tarot_users WHERE user_id = ?', [userId]);
        let user = rows[0];
        // 时间格式化
        [
            'registration_time',
            'last_login_time',
            'created_at',
            'updated_at',
            'subscription_start_date',
            'subscription_expiry_date',
            'invitation_time'
        ].forEach(field => {
            user[field] = user[field]
                ? moment(user[field])
                    .tz("Asia/Shanghai")
                    .format("YYYY-MM-DD HH:mm:ss")
                : null;
        });
        return { user, isNew: true };
    }
}
router.post('/getUserInfo', async (req, res) => {
    try {
        const { user_id } = req.body;

        if (!user_id) {
            return res.status(400).json({ code: 400, message: "user_id必填" });
        }

        const rows = await mysqlQueryTarot(`SELECT * FROM tarot_users WHERE user_id = ?`, [user_id]);
        if (!rows || rows.length === 0) {
            return res.status(404).json({ code: 404, message: "用户不存在" });
        }
        let user = rows[0];
        // 格式化时间字段
        [
            'registration_time',
            'last_login_time',
            'created_at',
            'updated_at',
            'subscription_start_date',
            'subscription_expiry_date',
            'invitation_time'
        ].forEach(field => {
            user[field] = user[field]
                ? moment(user[field]).tz("Asia/Shanghai").format("YYYY-MM-DD HH:mm:ss")
                : null;
        });

        return res.send({
            code: 0,
            message: 'success',
            data: user
        });
    } catch (e) {
        console.error('获取用户信息异常:', e);
        res.status(500).json({ code: 500, message: "服务器异常" });
    }
});
router.post('/editUserInfo', async (req, res) => {
    try {
        const {
            user_id,
            username,
            avatar_url,
            gender,
            birth_date,
            zodiac_sign,
            interests,
            country,
            city
        } = req.body;

        if (!user_id) {
            return res.status(400).json({ code: 400, message: "缺少user_id" });
        }

        // 收集要修改的字段和值
        const fieldMap = {
            username,
            avatar_url,
            gender,
            birth_date,
            zodiac_sign,
            interests,
            country,
            city
        };
        // 过滤掉未传 undefined 的，只保留传入的字段
        const keys = Object.keys(fieldMap).filter(key => fieldMap[key] !== undefined);

        // 必须只传一个字段，或根据你的业务允许一次多个字段
        if (keys.length === 0) {
            return res.status(400).json({ code: 400, message: "没有要修改的内容" });
        }
        // if (keys.length > 1) {
        //     return res.status(400).json({ code: 400, message: "每次只能修改一个字段" });
        // }

        const field = keys[0];
        const value = fieldMap[field];

        // 构造SQL并更新
        const sql = `UPDATE tarot_users SET ${field} = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?`;
        await mysqlQueryTarot(sql, [value, user_id]);

        res.json({ code: 0, message: `更新${field}成功` });
    } catch (e) {
        console.error('修改资料异常:', e);
        res.status(500).json({ code: 500, message: '服务器异常' });
    }
});

//注销账号
router.post('/user/account/delete', async (req, res) => {
    const { user_id } = req.body;
    if (!user_id) {
        return res.status(400).json({ code: 400, msg: 'user_id必传' });
    }
    try {
        // 检查用户是否存在
        const rows = await mysqlQueryTarot('SELECT user_id FROM tarot_users WHERE user_id = ?', [user_id]);
        if (!rows.length) {
            return res.status(404).json({ code: 404, msg: '账号不存在或已注销' });
        }
        // 注销（物理删除）
        await mysqlQueryTarot('DELETE FROM tarot_users WHERE user_id = ?', [user_id]);
        // 你可以按需还加上联删其它附属表内的用户数据（如Recipe_UserRecipe、Recipe_Collect等，需级联delete处理）
        return res.send({ code: 0, msg: '账号已注销' });
    } catch (e) {
        console.error('[user/account/delete] 注销失败:', e);
        res.status(500).json({ code: 500, msg: '服务器异常' });
    }
});
// 用户主动在客户端购买会员时调用（不限首次，也不限上次是否因取消掉过）
router.post('/subscription/activateByClient', async (req, res) => {
    const chinaTime = new Date();
    const { user_id, body } = req.body;
    const moment = require('moment-timezone');

    if (!user_id || !body || typeof body !== 'object') {
        return res.status(400).json({ code: 400, message: '参数错误：user_id 和 body 必须提供' });
    }

    // 1. 检查当前用户是否存在
    const rowsx = await mysqlQuery(`SELECT * FROM tarot_users WHERE user_id = ?`, [user_id]);
    if (!rowsx || rowsx.length === 0) {
        return res.status(404).json({ code: 404, message: "用户不存在" });
    }

    const {
        productId,                // 商品ID
        transactionId,            // 当前订单ID
        originalTransactionId,    // 原始订单ID
        purchaseDate,             // 当前订单购买时间（时间戳，毫秒）
        subscriptionExpirationDate, // 订阅过期时间（时间戳，毫秒）
        isTrialPeriod             // 是否试用
    } = body;

    // --- 新增逻辑，开始 ---
    try {
        // a. 检查数据库是否有其他用户占用此 originalTransactionId
        const usersWithThisOTID = await mysqlQuery(
            `SELECT user_id FROM tarot_users WHERE original_transaction_id = ? AND user_id != ?`,
            [originalTransactionId, user_id]
        );
        if (usersWithThisOTID && usersWithThisOTID.length > 0) {
            // 有，则批量更新这些账号为普通用户
            await mysqlQuery(
                `UPDATE tarot_users SET
                    user_type = 'regular',
                    subscription_start_date = NULL, 
                    subscription_expiry_date = NULL,
                    current_product_id = NULL,
                    transaction_id = NULL,
                    original_transaction_id = NULL
                WHERE original_transaction_id = ? AND user_id != ?`,
                [originalTransactionId, user_id]
            );
        }
        // --- 新增逻辑，结束 ---

        // b. 继续正常会员激活/续费逻辑
        let newUserType;
        let now = Date.now();
        const expires = Number(subscriptionExpirationDate || 0);

        if (isTrialPeriod === true || isTrialPeriod === 'true') {
            newUserType = expires > now ? 'trial_member' : 'regular';
        } else {
            newUserType = expires > now ? 'renewal_member' : 'regular';
        }

        // c. 时间戳转北京时间字符串辅助函数
        function toBeijingStr(ts) {
            if (!ts) return null;
            return moment(Number(ts)).tz("Asia/Shanghai").format("YYYY-MM-DD HH:mm:ss");
        }
        const subscription_start_date = toBeijingStr(purchaseDate);
        const subscription_expiry_date = toBeijingStr(subscriptionExpirationDate);

        // d. 更新目标用户的会员相关信息（会员身份、订单信息、订阅起止时间等）
        const updateSQL = `
            UPDATE tarot_users SET
                user_type = ?,
                subscription_start_date = ?,
                subscription_expiry_date = ?,
                current_product_id = ?,
                transaction_id = ?,
                original_transaction_id = ?
            WHERE user_id = ?
        `;
        const updateParams = [
            newUserType,
            subscription_start_date,
            subscription_expiry_date,
            productId || null,
            transactionId || null,
            originalTransactionId || null,
            user_id
        ];
        await mysqlQuery(updateSQL, updateParams);

        // e. 查询并格式化最新用户数据
        const users = await mysqlQuery('SELECT * FROM tarot_users WHERE user_id = ?', [user_id]);
        const updatedUser = users[0];
        [
            "subscription_start_date", "subscription_expiry_date",
            "registration_time", "last_login_time", "invitation_time", "created_at", "updated_at"
        ].forEach(field => {
            if (updatedUser[field]) {
                updatedUser[field] = moment(updatedUser[field]).tz("Asia/Shanghai").format("YYYY-MM-DD HH:mm:ss");
            }
        });

        return res.json({ code: 0, msg: '会员激活/购买成功', data: updatedUser });

    } catch (e) {
        console.error('activateByClient error:', e);
        res.status(500).json({ code: 500, message: '服务器错误' });
    }
});

//邀请裂变
// 绑定邀请码接口
router.post('/bindInvitation', async (req, res) => {
    const {
        user_id,
        inviterCode
    } = req.body;
    let conn;
    try {
        // 1. 获取连接，开启事务
        conn = await new Promise((resolve, reject) => {
            poolTarot.getConnection((err, c) => err ? reject(err) : resolve(c));
        });
        await conn.beginTransaction();

        // 2. 查询被邀请人&加锁
        const invitees = await mysqlQueryWithConn(
            'SELECT * FROM tarot_users WHERE user_id=? FOR UPDATE',
            [user_id],
            conn
        );
        const invitee = invitees?.[0];
        if (!invitee) {
            await conn.rollback();
            return res.send({
                code: 1,
                msg: '用户不存在'
            });
        }
        if (invitee.invited_by) {
            await conn.rollback();
            return res.send({
                code: 2,
                msg: '已绑定邀请人'
            });
        }

        // 3. 查询邀请人&加锁
        const inviters = await mysqlQueryWithConn(
            'SELECT * FROM tarot_users WHERE invitation_code=? FOR UPDATE',
            [inviterCode],
            conn
        );
        const inviter = inviters?.[0];
        if (!inviter) {
            await conn.rollback();
            return res.send({
                code: 3,
                msg: '邀请码无效'
            });
        }
        if (inviter.user_id === user_id) {
            await conn.rollback();
            return res.send({
                code: 4,
                msg: '不能邀请自己'
            });
        }

        // 检查邀请人注册时间早于被邀请人
        if (new Date(inviter.registration_time) >= new Date(invitee.registration_time)) {
            await conn.rollback();
            return res.send({
                code: 5,
                msg: '邀请人注册时间需早于被邀请人'
            });
        }

        // == 4. 固定写死奖励天数 ==
        const inviteeRewardDays = 7;

        // == 5. 被邀请人部分不变（你可以保持对会员身份善后判断）==
        const now = new Date();

        // 6. 记录被邀请人的奖励兑换日志
        await mysqlQueryWithConn(
            "INSERT INTO tarot_invite_reward_redeem_records (user_id, redeem_days, redeem_type, redeem_time) VALUES (?, ?, ?, ?)",
            [user_id, inviteeRewardDays, 0, now],
            conn
        );
        // 更新被邀请人：身份判定升级
        let inviteeUpdates = ['invited_by=?', 'inviter_name=?', 'invitation_time=?'];
        let inviteeParams = [inviter.user_id, inviter.username, now];

        if (invitee.user_type === 'regular') {
            // 普通用户->临时会员
            const expiry = moment(now).add(inviteeRewardDays, 'days').format("YYYY-MM-DD HH:mm:ss");
            inviteeUpdates.push(
                "user_type='temporary_member'",
                "subscription_start_date=?",
                "subscription_expiry_date=?",
            );
            inviteeParams.push(now, expiry);
        } else if (invitee.user_type === 'temporary_member') {
            // 临时会员续期
            let baseTime = moment.max(
                moment(now),
                invitee.subscription_expiry_date ? moment(invitee.subscription_expiry_date) : moment(now)
            );
            const expiry = baseTime.add(inviteeRewardDays, 'days').format("YYYY-MM-DD HH:mm:ss");
            inviteeUpdates.push("subscription_expiry_date=?");
            inviteeParams.push(expiry);
        } else if (
            invitee.user_type === 'trial_member' ||
            invitee.user_type === 'renewal_member'
        ) {
            // 体验/续费会员，奖励暂存
            inviteeUpdates.push("post_vip_pending_days=post_vip_pending_days+?");
            inviteeParams.push(inviteeRewardDays);
        }
        // 公用部分
        inviteeUpdates.push("invite_reward_redeemed_days=invite_reward_redeemed_days+?");
        inviteeParams.push(inviteeRewardDays);

        inviteeParams.push(user_id);
        await mysqlQueryWithConn(
            `UPDATE tarot_users SET ${inviteeUpdates.join(',')} WHERE user_id=?`,
            inviteeParams,
            conn
        );
        // ----- [邀请人：加邀请人数，判定奖励] -----
        await mysqlQueryWithConn(
            'UPDATE tarot_users SET invited_count=invited_count+1 WHERE user_id=?',
            [inviter.user_id],
            conn
        );

        // 再查一次邀请人最新数据
        const [inviterLatest] = await mysqlQueryWithConn(
            'SELECT invited_count, user_type, subscription_expiry_date FROM tarot_users WHERE user_id=? FOR UPDATE',
            [inviter.user_id],
            conn
        );

        // 查询邀请人已获邀请奖励记录（针对档位），需要一张奖励发放记录表或者用字段记录
        const inviteLevels = [
            { count: 3, days: 7, level: 1 },            // 3人 送7天
            { count: 5, days: 30, level: 2 },           // 5人 送1个月（30天）
            { count: 10, days: 90, level: 3 },          // 10人 送3个月
            { count: 20, days: 365, level: 4 }          // 20人 送1年
        ];
        // 假设 transir_invite_reward_grant_log (user_id, level) 记录谁获得了什么级别奖励
        const grantedRows = await mysqlQueryWithConn(
            'SELECT level FROM tarot_invite_reward_grant_log WHERE user_id=?',
            [inviter.user_id],
            conn
        );
        const grantedLevels = grantedRows.map(r => r.level);

        // 检查需要发放的奖励
        const unlockedLevels = [];
        for (let item of inviteLevels) {
            if (
                inviterLatest.invited_count >= item.count &&
                !grantedLevels.includes(item.level)
            ) {
                unlockedLevels.push(item); // 这个档位可以发奖励
            }
        }

        for (let gift of unlockedLevels) {
            // 发放奖励
            // 记录日志
            await mysqlQueryWithConn(
                "INSERT INTO tarot_invite_reward_grant_log (user_id, level, grant_days, grant_time) VALUES (?, ?, ?, ?)",
                [inviter.user_id, gift.level, gift.days, now],
                conn
            );

            // ----发放会员奖励（判定当前身份）----
            let updates = [];
            let params = [];
            // 取邀请人最新 user_type, subscription_expiry_date。以你给被邀请人逻辑类比处理即可
            if (inviterLatest.user_type === 'regular') {
                // 升级为临时会员
                const expiry = moment(now).add(gift.days, 'days').format("YYYY-MM-DD HH:mm:ss");
                updates.push(
                    "user_type='temporary_member'",
                    "subscription_start_date=?",
                    "subscription_expiry_date=?"
                );
                params.push(now, expiry);
            } else if (inviterLatest.user_type === 'temporary_member') {
                let baseTime = moment.max(
                    moment(now),
                    inviterLatest.subscription_expiry_date ? moment(inviterLatest.subscription_expiry_date) : moment(now)
                );
                const expiry = baseTime.add(gift.days, 'days').format("YYYY-MM-DD HH:mm:ss");
                updates.push(
                    "subscription_expiry_date=?"
                );
                params.push(expiry);
            } else if (
                inviterLatest.user_type === 'trial_member' ||
                inviterLatest.user_type === 'renewal_member'
            ) {
                // 体验/续费会员，奖励暂存，等待会员到期自动激活
                updates.push("post_vip_pending_days=post_vip_pending_days+?");
                params.push(gift.days);
            }
            // 公用部分：兑换奖励相关字段更新
            updates.push("invite_reward_redeemed_days=invite_reward_redeemed_days+?");
            params.push(gift.days);

            params.push(inviter.user_id);
            await mysqlQueryWithConn(
                `UPDATE tarot_users SET ${updates.join(',')} WHERE user_id=?`,
                params,
                conn
            );

            // 也可以写发放奖励明细日志（区分来源）
            await mysqlQueryWithConn(
                "INSERT INTO tarot_invite_reward_redeem_records (user_id, redeem_days, redeem_type, redeem_time) VALUES (?, ?, ?, ?)",
                [inviter.user_id, gift.days, 1, now], // redeem_type=1:邀请进阶奖
                conn
            );
        }
        // ---- 邀请人普通邀请奖励本步骤可以省略，不再另加奖励天数 ----


        // 9. 记录邀请行为
        await mysqlQueryWithConn(
            'INSERT INTO tarot_invitation_records (inviter_user_id, invitee_user_id, invitee_username, invitee_avatar_url) VALUES (?,?,?,?)',
            [inviter.user_id, invitee.user_id, invitee.username, invitee.avatar_url],
            conn
        );

        // 10. 提交事务
        await conn.commit();
        return res.send({
            code: 0,
            msg: '绑定成功，奖励已自动激活'
        });
    } catch (e) {
        if (conn) await conn.rollback();
        return res.send({
            code: 500,
            msg: e.message || '服务器错误'
        });
    } finally {
        if (conn) conn.release();
    }
});



//多模型提示词管理
function normalizeVersion(version) {
    if (typeof version !== 'string') throw new Error('版本号必须为字符串');

    const cleanVer = version.replace(/^v/i, '');
    const parts = cleanVer.split('.').slice(0, 3);
    if (parts.some(p => !/^\d+$/.test(p))) {
        throw new Error(`非法版本号格式: ${version}`);
    }
    while (parts.length < 3) parts.push('0');
    return parts.map(p => p.padStart(6, '0')).join('.');
}

router.post('/modelConfig/list', async (req, res) => {
    let { page = 1, pageSize = 10, version } = req.body;
    page = parseInt(page) || 1;
    pageSize = parseInt(pageSize) || 10;
    const offset = (page - 1) * pageSize;

    let whereClauses = ['status = 1'];
    let params = [];

    try {
        if (version) {
            try {
                const normVer = normalizeVersion(version);
                whereClauses.push(`min_version_norm <= ?`);
                whereClauses.push(`max_version_norm >= ?`);
                params.push(normVer, normVer);
            } catch (err) {
                return res.status(400).json({ code: 400, msg: err.message });
            }
        }

        const whereSQL = whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : '';

        const totalRows = await mysqlQueryTarot(
            `SELECT COUNT(*) as total FROM tarot_feature_config ${whereSQL}`,
            params
        );
        const listRows = await mysqlQueryTarot(
            `SELECT * FROM tarot_feature_config ${whereSQL} ORDER BY id DESC LIMIT ?, ?`,
            [...params, offset, pageSize]
        );

        // 时间、JSON字段处理
        const list = listRows.map(row => {
            // 格式化时间字段为北京时间
            ['created_at', 'updated_at'].forEach(fld => {
                if (row[fld]) {
                    // 确保转为北京时间
                    row[fld] = moment(row[fld]).tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
                }
            });
            // feature_list字段主动parse
            if (row.feature_list && typeof row.feature_list === 'string') {
                try {
                    row.feature_list = JSON.parse(row.feature_list);
                } catch (e) {
                    row.feature_list = []; // 或者null
                }
            }
            return row;
        });

        return res.send({
            code: 0,
            msg: '获取成功',
            data: {
                list,
                total: totalRows[0].total
            }
        });
    } catch (e) {
        console.error('[modelConfig/list] 获取失败:', e);
        res.status(500).json({ code: 500, msg: '服务器异常' });
    }
});
router.post('/modelConfig/getLatest', async (req, res) => {
    let { version, pk } = req.body;
    if (!version) {
        return res.status(400).json({ code: 400, msg: 'version为必传参数' });
    }
    if (!pk) {
        return res.status(400).json({ code: 400, msg: 'pk为必传参数' });
    }
    // 假定normalizeVersion可正常调用
    let versionNorm;
    try {
        versionNorm = normalizeVersion(version);
    } catch (e) {
        return res.status(400).json({ code: 400, msg: e.message || 'version格式非法' });
    }

    try {
        // 查可用的，按id倒序1条
        const rows = await mysqlQueryTarot(
            `SELECT * FROM tarot_feature_config 
             WHERE status=1 AND pk=? AND min_version_norm <= ? AND max_version_norm >= ? 
             ORDER BY id DESC LIMIT 1`,
            [pk, versionNorm, versionNorm]
        );
        if (!rows.length) {
            return res.send({ code: 0, msg: '暂无配置', data: [] });
        }
        const row = rows[0];

        // 格式化时间（北京时间）
        ['created_at', 'updated_at'].forEach(fld => {
            if (row[fld]) {
                row[fld] = moment(row[fld]).tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
            }
        });

        // feature_list转JSON
        if (row.feature_list && typeof row.feature_list === 'string') {
            try {
                row.feature_list = JSON.parse(row.feature_list);
            } catch (e) {
                row.feature_list = [];
            }
        }

        // 去掉多余字段
        delete row.min_version_norm;
        delete row.max_version_norm;
        delete row.status;

        return res.send({
            code: 0,
            msg: '获取成功',
            data: row
        });
    } catch (e) {
        console.error('[modelConfig/getLatest] 失败:', e);
        res.status(500).json({ code: 500, msg: '服务器异常' });
    }
});
router.post('/modelConfig/switchEnv', async (req, res) => {
    const { id, pk } = req.body;
    if (!id || !pk) {
        return res.status(400).json({ code: 400, msg: 'id 和 pk 必填' });
    }
    try {
        const rows = await mysqlQueryTarot('SELECT id FROM tarot_feature_config WHERE id=?', [id]);
        if (!rows.length) {
            return res.status(404).json({ code: 404, msg: '该配置不存在' });
        }
        await mysqlQueryTarot('UPDATE tarot_feature_config SET pk=? WHERE id=?', [pk, id]);
        res.send({ code: 0, msg: '环境切换成功' });
    } catch (e) {
        res.status(500).json({ code: 500, msg: '环境切换失败' });
    }
});
/**
 * 保存配置（有 id 就更新，没有 id 就新增）
 * POST /modelConfig/save
 * 参数：
 *  - id (可选, 有则更新, 无则新增)
 *  - min_version, max_version, feature_list, status (status 默认 1)
 */
router.post('/modelConfig/save', async (req, res) => {
    let { id, min_version, max_version, feature_list, status = 1 } = req.body;

    if (!min_version || !max_version || !feature_list) {
        return res.status(400).json({ code: 400, msg: 'min_version, max_version, feature_list 为必填' });
    }

    try {
        // feature_list 转字符串
        if (typeof feature_list !== 'string') {
            feature_list = JSON.stringify(feature_list);
        }

        // 版本号规范化存储
        let min_version_norm, max_version_norm;
        try {
            min_version_norm = normalizeVersion(min_version);
            max_version_norm = normalizeVersion(max_version);
        } catch (err) {
            return res.status(400).json({ code: 400, msg: err.message });
        }

        if (id) {
            // 更新
            const rows = await mysqlQueryTarot('SELECT id FROM tarot_feature_config WHERE id = ?', [id]);
            if (!rows.length) {
                return res.status(404).json({ code: 404, msg: '配置不存在' });
            }
            await mysqlQueryTarot(
                `UPDATE tarot_feature_config 
                 SET min_version=?, min_version_norm=?, max_version=?, max_version_norm=?, feature_list=?, status=?
                 WHERE id=?`,
                [min_version, min_version_norm, max_version, max_version_norm, feature_list, status, id]
            );
            return res.send({ code: 0, msg: '修改成功' });
        } else {
            // 新增
            await mysqlQueryTarot(
                `INSERT INTO tarot_feature_config 
                 (min_version, min_version_norm, max_version, max_version_norm, feature_list, status) 
                 VALUES (?, ?, ?, ?, ?, ?)`,
                [min_version, min_version_norm, max_version, max_version_norm, feature_list, status]
            );
            return res.send({ code: 0, msg: '新增成功' });
        }
    } catch (e) {
        console.error('[modelConfig/save] 失败:', e);
        res.status(500).json({ code: 500, msg: '服务器异常' });
    }
});

/**
 * 4. 删除配置
 * POST /config/delete
 * 参数：id
 */
router.post('/modelConfig/delete', async (req, res) => {
    const { id } = req.body;
    if (!id) {
        return res.status(400).json({ code: 400, msg: 'id必传' });
    }

    try {
        const rows = await mysqlQueryTarot('SELECT id FROM tarot_feature_config WHERE id = ?', [id]);
        if (!rows.length) {
            return res.status(404).json({ code: 404, msg: '配置不存在或已删除' });
        }

        await mysqlQueryTarot('DELETE FROM tarot_feature_config WHERE id = ?', [id]);

        return res.send({ code: 0, msg: '删除成功' });
    } catch (e) {
        console.error('[config/delete] 失败:', e);
        res.status(500).json({ code: 500, msg: '服务器异常' });
    }
});
//后台用的 肯定语入库
router.post('/affirmation/insertBatch', async (req, res) => {
    const { list } = req.body; // [{theme,language,moon_phase,sentence}]
    if (!Array.isArray(list) || !list.length)
        return res.send({ code: 2, msg: '内容不能为空' });
    try {
        for (const item of list) {
            // 主题和月相映射
            let mappedTheme = mapping[item.theme] || item.theme;
            let mappedMoonPhase = moonPhaseMapping[item.moon_phase] || item.moon_phase || '';

            await mysqlQueryTarot('INSERT INTO tarot_affirmations (id, theme, language, moon_phase, sentence, status) VALUES (?, ?, ?, ?, ?, ?)', [
                utils.createId(), // id生成方法
                mappedTheme,
                item.language,
                mappedMoonPhase,
                item.sentence,
                1 // status
            ]);
        }
        res.send({ code: 0, msg: 'ok' });
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
});





/**
 * 获取用户已收藏的肯定语id集合
 * @param {string} user_id - 用户ID
 * @returns {Promise<Set>} 收藏的肯定语ID的Set
 */
async function getUserFavoriteAffirmationIdSet(user_id) {
    if (!user_id) return new Set();
    const rows = await mysqlQueryTarot(
        'SELECT affirmation_id FROM tarot_user_favorites WHERE user_id = ?',
        [user_id]
    );
    return new Set(rows.map(row => row.affirmation_id));
}
//根据主题请求肯定语

const MAX_RANDOM = 54;
const MAX_AI_RETRY = 10; // 最大AI重试次数
// 转换主题为英文，如果没匹配则返回原文
function convertThemeToEnglish(chineseTheme) {
    return mapping[chineseTheme] || chineseTheme;
}

// 转换月相为英文，如果没匹配则返回原文
function convertMoonPhaseToEnglish(chineseMoonPhase) {
    if (!chineseMoonPhase) return null;
    return moonPhaseMapping[chineseMoonPhase] || chineseMoonPhase;
}
// 解析AI返回的肯定语内容
function parseAffirmationsFromText(text, theme, language, moonPhase = null) {
    if (!text || typeof text !== 'string') return [];

    // 转换主题和月相为英文
    const englishTheme = convertThemeToEnglish(theme);
    const englishMoonPhase = convertMoonPhaseToEnglish(moonPhase);

    // 1. 完整去除所有 markdown 包裹
    text = text.replace(/```json/gi, '')
        .replace(/```/g, '')
        .trim();

    // 2. 尝试直接解析整个文本
    try {
        const obj = JSON.parse(text);
        if (obj && typeof obj === 'object'
            && obj.affirmations
            && Array.isArray(obj.affirmations.content)
            && obj.affirmations.content.length > 0) {
            return obj.affirmations.content.map(sentence => ({
                id: utils.createId(),
                theme: englishTheme,           // 存储英文主题
                language,
                moon_phase: englishMoonPhase,  // 存储英文月相
                sentence: typeof sentence === 'string' ? sentence : sentence?.text || sentence?.content || '',
                status: 1
            })).filter(item => item.sentence.trim());
        }
    } catch (e) { /* continue to next method */ }

    // 3. 匹配第一个包含 affirmations 的 JSON 对象
    let match = text.match(/\{[^]*?"affirmations"\s*:[^]*?\}/);
    if (match && match[0]) {
        try {
            const obj = JSON.parse(match[0]);
            if (obj && typeof obj === 'object'
                && obj.affirmations
                && Array.isArray(obj.affirmations.content)
                && obj.affirmations.content.length > 0) {
                return obj.affirmations.content.map(sentence => ({
                    id: utils.createId(),
                    theme: englishTheme,
                    language,
                    moon_phase: englishMoonPhase,
                    sentence: typeof sentence === 'string' ? sentence : sentence?.text || sentence?.content || '',
                    status: 1
                })).filter(item => item.sentence.trim());
            }
        } catch (e) { /* continue to next method */ }
    }

    // 4. 尝试所有可能的 JSON 对象
    let matches = text.match(/\{[^]*?\}/g);
    if (matches && matches.length) {
        for (let jsonStr of matches) {
            try {
                const obj = JSON.parse(jsonStr);
                if (obj && typeof obj === 'object'
                    && obj.affirmations
                    && Array.isArray(obj.affirmations.content)
                    && obj.affirmations.content.length > 0) {
                    return obj.affirmations.content.map(sentence => ({
                        id: utils.createId(),
                        theme: englishTheme,
                        language,
                        moon_phase: englishMoonPhase,
                        sentence: typeof sentence === 'string' ? sentence : sentence?.text || sentence?.content || '',
                        status: 1
                    })).filter(item => item.sentence.trim());
                }
            } catch (e) { /* try next */ }
        }
    }

    // 5. 尝试直接提取数组格式 ["句子1", "句子2", ...]
    let arrayMatch = text.match(/\[[^\]]*"[^"]+[^\]]*\]/);
    if (arrayMatch && arrayMatch[0]) {
        try {
            const arr = JSON.parse(arrayMatch[0]);
            if (Array.isArray(arr) && arr.length > 0) {
                return arr.map(sentence => ({
                    id: utils.createId(),
                    theme: englishTheme,
                    language,
                    moon_phase: englishMoonPhase,
                    sentence: typeof sentence === 'string' ? sentence : String(sentence),
                    status: 1
                })).filter(item => item.sentence.trim());
            }
        } catch (e) { /* continue */ }
    }


    return sentences.filter(item => item.sentence.trim());
}
/**
 * 通用AI内容解析
 * 只管结构，最大可能还原数据，无 affirmations、metadata、energy...业务字段限制
 * @param {string} text - AI返回的 content
 * @returns {Object} { json: 尽力解析出来的对象, arr: 若是数组或对象内数组, str: 若是单字符串, raw: 原始内容, type: 类型, allMatches: 所有可能JSON片段的数组 }
 */
function parseAIResult(text) {
    if (!text || typeof text !== 'string') return { json: null, arr: [], str: '', raw: text, type: 'empty', allMatches: [] };

    // 1. 去掉 markdown、收尾空格
    let cleanText = text.replace(/``` ?json/gi, '').replace(/```/g, '').trim();

    // 2. 先尝试整体 JSON.parse
    try {
        const obj = JSON.parse(cleanText);
        if (Array.isArray(obj)) {
            return { type: 'array', arr: obj, json: obj, str: '', raw: text, allMatches: [obj] };
        } else if (typeof obj === 'object' && obj !== null) {
            // 尝试把一级所有 key 为数组的都提取出来
            let foundArrs = [];
            Object.values(obj).forEach(val => { if (Array.isArray(val)) foundArrs.push(val); });
            return { type: 'object', json: obj, arr: foundArrs.flat(), str: '', raw: text, allMatches: [obj] };
        } else if (typeof obj === 'string') {
            return { type: 'string', str: obj, json: null, arr: [obj], raw: text, allMatches: [obj] };
        }
    } catch (e) { }

    // 3. 尝试文本内所有大JSON片段
    let matches = cleanText.match(/\{[\s\S]*?\}/g);
    let firstObj = null, allObjs = [];
    if (matches && matches.length) {
        for (let jsonStr of matches) {
            try {
                const obj = JSON.parse(jsonStr);
                allObjs.push(obj);
                if (!firstObj) firstObj = obj;
            } catch (e) { }
        }
        if (firstObj) {
            let arrs = [];
            // 把对象内所有数组再尝试提取
            const extractArrs = (o) => {
                if (Array.isArray(o)) arrs.push(o);
                else if (typeof o === 'object' && o !== null) Object.values(o).forEach(extractArrs);
            };
            extractArrs(firstObj);
            return { type: 'objectMultiJson', json: firstObj, arr: arrs.flat(), str: '', raw: text, allMatches: allObjs };
        }
    }

    // 4. 尝试直接数组片段
    let arrayMatch = cleanText.match(/\[[^\]]*"[^"]+[^\]]*\]/);
    if (arrayMatch && arrayMatch[0]) {
        try {
            const arr = JSON.parse(arrayMatch[0]);
            if (Array.isArray(arr)) {
                return { type: 'arrayFragment', arr, json: arr, str: '', raw: text, allMatches: [arr] };
            }
        } catch (e) { }
    }

    // 5. 兜底：分行筛选有意义的文本、带引号内容
    const arr = [];
    const lines = cleanText.split('\n');
    for (const line of lines) {
        if (line.startsWith('"') && line.endsWith('"')) {
            arr.push(line.slice(1, -1));
        } else if (line.length > 10) {
            arr.push(line.trim());
        }
    }
    return {
        type: arr.length ? 'lines' : 'unknown',
        arr,
        str: arr.join('\n'),
        json: null,
        raw: text,
        allMatches: []
    }
}
/**
 * 超简通用AI内容解析，只返回object或null
 * @param {string} text - AI流式或非流返回的内容
 * @returns {object|null}
 */
function parseAIToObject(text) {
    if (!text || typeof text !== 'string') return null;

    // 1. 去除 markdown 标记和首尾空格
    let cleanText = text
        .replace(/``` ?json/gi, '')
        .replace(/```/g, '')
        .trim();

    // 2. 去除 // 行尾注释
    //    方案A：逐行去，每行遇到 // 就删后面
    cleanText = cleanText.split('\n').map(line => {
        // 只保留 // 前面的内容
        const idx = line.indexOf('//');
        return idx > -1 ? line.slice(0, idx) : line;
    }).join('\n');

    // 3. 去除结尾多余的逗号（key/value结尾、对象/数组内）
    cleanText = cleanText
        // 去除对象最后一个元素的逗号（注意跨行情况）
        .replace(/,\s*([\}\]])/g, '$1');

    // 4. 全体空白行去除（有些AI会多输出回车）
    cleanText = cleanText
        .split('\n')
        .map(line => line.trim())
        .filter(line => !!line)
        .join('\n');

    // 5. 直接整体parse
    try {
        return JSON.parse(cleanText);
    } catch (e) { }

    // 6. 尝试片段parse
    const match = cleanText.match(/\{[\s\S]*\}/);
    if (match && match[0]) {
        try {
            let fixStr = match[0].replace(/,\s*([\}\]])/g, '$1');
            return JSON.parse(fixStr);
        } catch { }
    }

    // 7. 都不行，返回null
    return text;
}

// 流式调用单个模型的函数
async function callSingleModelStream(modelConfig, theme, language, moonPhase = null) {
    const { model, prompt: realPrompt } = modelConfig;

    console.log(`\n【AI模型-流式】${model}，【本次prompt】\n${realPrompt.slice(0, 50)}${realPrompt.length > 50 ? '...' : ''}`);
    if (moonPhase) {
        console.log(`【月相】${moonPhase}`);
    }

    const aiPayload = {
        model,
        messages: [{
            role: "user",
            content: [{ type: "text", text: realPrompt }]
        }],
        stream: true, // 开启流式
        response_format: { type: "text" },
        temperature: 0.7,
        max_tokens: 1024,
        top_p: 1,
        frequency_penalty: 0,
        top_k: 50
    };

    const t0 = Date.now();
    let fullContent = '';
    let firstChunkReceived = false;

    try {
        const response = await axios.post(
            "https://api-st.siliconflow.cn/v1/chat/completions",
            aiPayload,
            {
                timeout: 10000, // 增加超时时间，因为流式接收
                responseType: 'stream',
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Bearer sk-tcmxreifxtoyrbjjwcddwlxxqhieysozdluqfnoosmeoeeej`
                }
            }
        );

        return new Promise((resolve, reject) => {
            const chunks = [];
            let buffer = '';

            response.data.on('data', (chunk) => {
                if (!firstChunkReceived) {
                    const t1 = Date.now();
                    console.log(`[计时] 模型: ${model}，首个数据块接收耗时: ${t1 - t0}ms`);
                    firstChunkReceived = true;
                }

                buffer += chunk.toString();

                // 处理可能的分割数据
                const lines = buffer.split('\n');
                buffer = lines.pop() || ''; // 保留最后一个可能不完整的行

                for (const line of lines) {
                    if (line.trim()) {
                        const parsed = parseStreamChunk(line);
                        if (parsed.isDone) {
                            // 流结束
                            const t1 = Date.now();
                            console.log(`[计时] 模型: ${model}，流式接收总耗时: ${t1 - t0}ms`);

                            if (!fullContent.trim()) {
                                const error = `[${model}] 流式接收内容为空`;
                                console.error(`[FAIL] ${error}`);
                                resolve({ success: false, error, affirmations: [] });
                                return;
                            }

                            console.log(`[${model}] 流式接收完成，总字符数: ${fullContent.length}`);
                            // 解析完整内容
                            const newAffirmations = parseAffirmationsFromText(fullContent, theme, language, moonPhase);

                            if (newAffirmations.length > 0) {
                                console.log(`✓ ${model} 流式成功生成 ${newAffirmations.length} 条肯定语，前两条预览:`);
                                for (let i = 0; i < Math.min(newAffirmations.length, 2); i++) {
                                    const moonInfo = newAffirmations[i].moon_phase ? ` [${newAffirmations[i].moon_phase}]` : '';
                                    console.log(`  ${i + 1}. ${newAffirmations[i].sentence}${moonInfo}`);
                                }
                                resolve({ success: true, error: null, affirmations: newAffirmations });
                            } else {
                                const error = `[${model}] 流式返回格式解析失败，原始内容：${fullContent.substring(0, 100)}...`;
                                console.error(`[FAIL] ${error}`);
                                resolve({ success: false, error, affirmations: [] });
                            }
                            return;
                        } else if (parsed.content) {
                            fullContent += parsed.content;
                            // 可选：实时输出接收到的内容（用于调试）
                            // process.stdout.write(parsed.content);
                        }
                    }
                }
            });

            response.data.on('error', (err) => {
                const t1 = Date.now();
                console.log(`[计时] 模型: ${model}，流式接收错误耗时: ${t1 - t0}ms`);
                const error = `[${model}] 流式接收错误: ${err.message}`;
                console.error(`[FAIL] ${error}`);
                resolve({ success: false, error, affirmations: [] });
            });

            response.data.on('end', () => {
                // 处理剩余的buffer
                if (buffer.trim()) {
                    const parsed = parseStreamChunk(buffer);
                    if (parsed.content) {
                        fullContent += parsed.content;
                    }
                }

                // 如果没有收到 [DONE] 标记，在这里处理结束
                if (fullContent.trim()) {
                    const t1 = Date.now();
                    console.log(`[计时] 模型: ${model}，流式接收结束耗时: ${t1 - t0}ms`);
                    console.log(`[${model}] 流接收结束，总字符数: ${fullContent.length}`);

                    const newAffirmations = parseAffirmationsFromText(fullContent, theme, language, moonPhase);

                    if (newAffirmations.length > 0) {
                        console.log(`✓ ${model} 流式成功生成 ${newAffirmations.length} 条肯定语`);
                        resolve({ success: true, error: null, affirmations: newAffirmations });
                    } else {
                        const error = `[${model}] 流式返回格式解析失败`;
                        resolve({ success: false, error, affirmations: [] });
                    }
                } else {
                    const error = `[${model}] 流式接收结束但内容为空`;
                    resolve({ success: false, error, affirmations: [] });
                }
            });
        });

    } catch (e) {
        const t1 = Date.now();
        console.log(`[计时] 模型: ${model}，流式请求异常耗时: ${t1 - t0}ms`);

        let error;
        if (e.code === "ECONNABORTED") {
            error = `[${model}] 5秒内无响应，快速切换模型`;
            console.error(`[FAIL] ${error}`);
        } else {
            error = `[${model}] 流式请求异常: ${e.response?.data?.msg || e.message}`;
            console.error(`[FAIL] ${error}`);
        }

        return { success: false, error, affirmations: [] };
    }
}
/**
 * 通用AI流式单模型调用（可用于肯定语能量分析、理解、推荐等）
 * @param {Object} modelConfig  - {model, prompt}
 * @param {string} content      - 用户输入的内容，如肯定语、问题等
 * @param {Function} parseResult(optional) - 自定义解析AI原始返回内容函数，(fullContent, {model, prompt, content}) => { ... }
 * @returns {Promise<{success, aiResult, error, parsed}>}
 */
async function callAIModelStream(modelConfig, content, parseResult = null) {
    const { model, prompt: realPrompt } = modelConfig;
    console.log(`\n【AI模型-流式】${model}，【本次prompt】\n${realPrompt.slice(0, 80)}${realPrompt.length > 80 ? '...' : ''}`);

    const aiPayload = {
        model,
        messages: [{
            role: "user",
            content: [{ type: "text", text: realPrompt }]
        }],
        stream: true,
        response_format: { type: "text" },
        temperature: 0.7,
        max_tokens: 1024,
        top_p: 1,
        frequency_penalty: 0,
        top_k: 50
    };

    const t0 = Date.now();
    let fullContent = '';
    let firstChunkReceived = false;

    try {
        const response = await axios.post(
            "https://api-st.siliconflow.cn/v1/chat/completions",
            aiPayload,
            {
                timeout: 10000, // 流式延长
                responseType: 'stream',
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Bearer sk-tcmxreifxtoyrbjjwcddwlxxqhieysozdluqfnoosmeoeeej`
                }
            }
        );

        return new Promise((resolve, reject) => {
            let buffer = '';
            response.data.on('data', (chunk) => {
                if (!firstChunkReceived) {
                    const t1 = Date.now();
                    console.log(`[计时] 模型: ${model}，首个数据块接收耗时: ${t1 - t0}ms`);
                    firstChunkReceived = true;
                }
                buffer += chunk.toString();
                const lines = buffer.split('\n');
                buffer = lines.pop() || '';
                for (const line of lines) {
                    if (line.trim()) {
                        const parsed = parseStreamChunk(line);
                        if (parsed.isDone) {
                            const t1 = Date.now();
                            console.log(`[计时] 模型: ${model}，流式接收总耗时: ${t1 - t0}ms`);
                            if (!fullContent.trim()) {
                                const error = `[${model}] 流式接收内容为空`;
                                console.error(`[FAIL] ${error}`);
                                resolve({ success: false, error, aiResult: '', parsed: null });
                                return;
                            }
                            console.log(`[${model}] 流式接收完成，总字符数: ${fullContent.length}`);

                            // --- 通用结果解析 ---
                            let parsedResult = null;
                            if (typeof parseResult === 'function') {
                                parsedResult = parseResult(fullContent, { model, prompt: realPrompt, content });
                            }
                            resolve({
                                success: true,
                                aiResult: fullContent,
                                error: null,
                                parsed: parsedResult
                            });
                            return;
                        } else if (parsed.content) {
                            fullContent += parsed.content;
                        }
                    }
                }
            });

            response.data.on('error', (err) => {
                const t1 = Date.now();
                const error = `[${model}] 流式接收错误: ${err.message}`;
                console.error(`[FAIL] ${error}`);
                resolve({ success: false, aiResult: '', error, parsed: null });
            });

            response.data.on('end', () => {
                // 处理剩余buffer
                if (buffer.trim()) {
                    const parsed = parseStreamChunk(buffer);
                    if (parsed.content) fullContent += parsed.content;
                }
                if (fullContent.trim()) {
                    const t1 = Date.now();
                    console.log(`[计时] 模型: ${model}，流式接收结束耗时: ${t1 - t0}ms`);
                    console.log(`[${model}] 流接收结束，总字符数: ${fullContent.length}`);
                    let parsedResult = null;
                    if (typeof parseResult === 'function') {
                        parsedResult = parseResult(fullContent, { model, prompt: realPrompt, content });
                    }
                    resolve({
                        success: true,
                        aiResult: fullContent,
                        error: null,
                        parsed: parsedResult
                    });
                } else {
                    const error = `[${model}] 流式接收结束但内容为空`;
                    resolve({ success: false, aiResult: '', error, parsed: null });
                }
            });
        });

    } catch (e) {
        const t1 = Date.now();
        let error;
        if (e.code === "ECONNABORTED") {
            error = `[${model}] 30秒内无响应，快速切换模型`;
        } else {
            error = `[${model}] 流式请求异常: ${e.response?.data?.msg || e.message}`;
        }
        console.error(`[FAIL] ${error}`);
        return { success: false, aiResult: '', error, parsed: null };
    }
}

// 调用AI生成肯定语 - 完整版本
async function generateAffirmationsWithAI(feature, theme, language, needCount, moonPhase = null) {
    const maxRetry = MAX_AI_RETRY;
    let allAiAffirmations = [];
    let lastError = null;
    let retryCount = 0;

    // 记录成功的模型配置，用于后续优先使用
    let successfulConfig = null;

    while (allAiAffirmations.length < needCount && retryCount < maxRetry) {
        retryCount++;
        console.log(`AI生成尝试 ${retryCount}/${maxRetry}，当前已生成 ${allAiAffirmations.length}/${needCount} 条`);

        let success = false;

        // 如果有成功的配置，优先使用
        if (successfulConfig) {
            console.log(`使用上次成功的模型: ${successfulConfig.model}`);
            const result = await callSingleModelStream(successfulConfig, theme, language, moonPhase);

            if (result.success) {
                allAiAffirmations = allAiAffirmations.concat(result.affirmations);
                console.log(`成功模型再次生成 ${result.affirmations.length} 条肯定语`);
                success = true;
            } else {
                console.log(`上次成功的模型失败，原因: ${result.error}`);
                lastError = result.error;
                // 清除失败的成功配置，重新轮询
                successfulConfig = null;
            }
        }

        // 如果没有成功配置或成功配置失败了，进行完整轮询
        if (!success && !successfulConfig) {
            for (const group of feature.modelGroups) {
                if (success) break;

                let prompt = group.prompt || '';
                let extraPrompt = group.extraPrompt || '';
                let realPrompt = prompt
                    + `\n肯定语主题 = "${theme}"\n输出语言 = "${language}"`;

                // 如果有月相，添加到 prompt 中
                if (moonPhase) {
                    realPrompt += `\n月相 = "${moonPhase}"`;
                }

                if (extraPrompt) realPrompt += '\n' + extraPrompt;

                const models = group.models || [];

                // models串行调用
                for (const model of models) {
                    if (success) break;

                    const modelConfig = { group, model, prompt: realPrompt };
                    const result = await callSingleModelStream(modelConfig, theme, language, moonPhase);
                    if (result.success) {
                        allAiAffirmations = allAiAffirmations.concat(result.affirmations);
                        console.log(`模型 ${model} 首次成功生成 ${result.affirmations.length} 条肯定语`);

                        // 记录成功的配置
                        successfulConfig = modelConfig;
                        success = true;
                        break;
                    } else {
                        lastError = result.error;
                    }
                }
            }
        }

        // 如果这轮没有成功生成任何内容，稍作延迟再重试
        if (!success) {
            console.log(`第 ${retryCount} 次尝试失败，等待1秒后重试...`);
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
    }

    return {
        affirmations: allAiAffirmations.slice(0, needCount),
        lastError,
        retryCount,
        successfulModel: successfulConfig?.model
    };
}
/**
 * 通用AI流式多模型内容生成器（如肯定语能量识别等）
 * 只遍历模型组、传入content，支持模型轮询和流式失败兜底
 * @param {Array} modelGroups - feature.modelGroups
 * @param {String} content - 用户输入内容（如肯定语）
 * @returns { aiResult: '', lastError, successfulModel }
 */
async function generateAIStreamContent({ modelGroups, content }) {
    let resultContent = '';
    let lastError = null;
    let successfulModel = null;

    for (const group of modelGroups) {
        let prompt = group.prompt || '';
        let extraPrompt = group.extraPrompt || '';
        // 支持模板插值，如果 prompt 含 {{content}}
        let realPrompt = prompt + '\n' + content

        if (extraPrompt) realPrompt += '\n' + extraPrompt;

        const models = group.models || [];
        for (const model of models) {
            const modelConfig = { group, model, prompt: realPrompt };
            const result = await callAIModelStream(modelConfig, content);
            if (result.success) {
                resultContent = result.aiResult;
                successfulModel = model;
                return {
                    aiResult: resultContent,
                    lastError: null,
                    successfulModel
                };
            } else {
                lastError = result.error;
            }
        }
    }
    // 全部模型失败
    return {
        aiResult: '', // 或 null
        lastError,
        successfulModel: null
    };
}
// 处理流式数据的解析函数
function parseStreamChunk(chunk) {
    const lines = chunk.split('\n').filter(line => line.trim());
    const results = [];

    for (const line of lines) {
        if (line.startsWith('data: ')) {
            const data = line.slice(6).trim();
            if (data === '[DONE]') {
                return { isDone: true, content: '' };
            }

            try {
                const parsed = JSON.parse(data);
                const content = parsed.choices?.[0]?.delta?.content || '';
                if (content) {
                    results.push(content);
                }
            } catch (e) {
                // 忽略解析错误的chunk
                continue;
            }
        }
    }

    return { isDone: false, content: results.join('') };
}
// 单主题-获得肯定语
router.post('/affirmation/randomList', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    // 请求参数
    let { theme, language, version, pk, moon_phase, user_id } = contentx;
    if (!theme || !language || !version || !pk) {
        return res.send({ code: 1, msg: '主题、语言、user_id、version、pk必填' });
    }
    // 归一化版本号
    let normVersion;
    try {
        normVersion = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }

    // 转换输入的主题和月相为英文
    const englishTheme = convertThemeToEnglish(theme);
    const englishMoonPhase = convertMoonPhaseToEnglish(moon_phase);

    if (moon_phase) {
        console.log(`月相转换: ${moon_phase} -> ${englishMoonPhase}`);
    }

    try {
        // Step 1. 使用英文主题和月相查询现有肯定语
        let querySQL, queryParams;

        if (englishMoonPhase) {
            // 如果有月相，查询指定月相的肯定语
            querySQL = `SELECT id, theme, language, moon_phase, sentence FROM tarot_affirmations 
                        WHERE theme=? AND language=? AND (moon_phase=? OR moon_phase IS NULL) AND status=1 
                        ORDER BY RAND() LIMIT ?`;
            queryParams = [englishTheme, language, englishMoonPhase, MAX_RANDOM];
        } else {
            // 如果没有月相，查询所有该主题的肯定语
            querySQL = `SELECT id, theme, language, moon_phase, sentence FROM tarot_affirmations 
                        WHERE theme=? AND language=? AND status=1 
                        ORDER BY RAND() LIMIT ?`;
            queryParams = [englishTheme, language, MAX_RANDOM];
        }

        let affirmations = await mysqlQueryTarot(querySQL, queryParams);
        console.log(`从数据库查询到 ${affirmations.length} 条肯定语，目标 ${MAX_RANDOM} 条`);

        // Step 2. 如果数量不够，AI生成补充
        if (affirmations.length < MAX_RANDOM) {
            const needCount = MAX_RANDOM - affirmations.length;

            // 2.1 查询 tarot_feature_config
            const configs = await mysqlQueryTarot(
                `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
                [pk, normVersion, normVersion]
            );
            if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

            let featureList;
            try {
                featureList = JSON.parse(configs[0].feature_list);
            } catch {
                return res.send({ code: 2, msg: '功能配置解析失败' });
            }

            const feature = featureList.find(f => f.featureName === "affirmation_generation");
            if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

            // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
            console.log(`开始AI流式生成，需要补充 ${needCount} 条肯定语`);
            const { affirmations: aiAffirmList, lastError, retryCount } = await generateAffirmationsWithAI(
                feature, theme, language, needCount, moon_phase // 传入原始中文参数
            );

            console.log(`AI流式生成完成，共重试 ${retryCount} 次，生成 ${aiAffirmList.length} 条`);

            // 2.3 保存AI生成的肯定语到数据库（已经是英文格式）
            if (aiAffirmList.length > 0) {
                for (const a of aiAffirmList) {
                    try {
                        await mysqlQueryTarot(
                            `INSERT INTO tarot_affirmations (id, theme, language, moon_phase, sentence, status, created_at)
                             VALUES (?, ?, ?, ?, ?, 1, NOW())`,
                            [a.id, englishTheme, language, a.moon_phase, a.sentence]
                        );
                        console.log(`✓ 入库成功: 主题=${englishTheme}, 月相=${a.moon_phase}, 句子=${a.sentence.substring(0, 30)}...`);
                    } catch (insertError) {
                        console.error(`✗ 插入肯定语失败: ${insertError.message}`, a);
                        // 继续插入其他数据，不因个别失败而中断
                    }
                }

                // 2.4 重新查询，获取完整列表
                affirmations = await mysqlQueryTarot(querySQL, queryParams);
                console.log(`重新查询后共有 ${affirmations.length} 条肯定语`);
            }

            // 2.5 如果仍然没有足够的数据
            if (affirmations.length === 0) {
                return res.send({
                    code: 4,
                    msg: `未能获取任何肯定语，AI流式生成失败。重试次数: ${retryCount}，最后错误: ${lastError || '未知错误'}`
                });
            }

            // 记录警告日志（数量不足但有部分数据）
            if (affirmations.length < MAX_RANDOM) {
                console.warn(`肯定语数量不足，期望 ${MAX_RANDOM} 条，实际 ${affirmations.length} 条。AI重试 ${retryCount} 次，最后错误: ${lastError}`);
            }
        }

        // Step 3. 在返回前再次打乱数据顺序
        console.log(`准备返回 ${affirmations.length} 条肯定语，执行二次随机打乱...`);
        const shuffledAffirmations = shuffleArray(affirmations);
        console.log(`✓ 数据打乱完成，前3条预览:`);
        for (let i = 0; i < Math.min(shuffledAffirmations.length, 3); i++) {
            const moonInfo = shuffledAffirmations[i].moon_phase ? ` [${shuffledAffirmations[i].moon_phase}]` : '';
            console.log(`  ${i + 1}. ${shuffledAffirmations[i].sentence.substring(0, 40)}...${moonInfo}`);
        }

        // 获取用户收藏的肯定语ID集合
        const favoriteSet = user_id ? await getUserFavoriteAffirmationIdSet(user_id) : new Set();
        const enhancedList = shuffledAffirmations.map(item => ({
            ...item,
            is_favorite: favoriteSet.has(item.id) ? 1 : 0 //1已 0未
        }));

        const encrypteds = encrypt(JSON.stringify(enhancedList), config.cryptoKey, config.cryptoIv);

        return res.send({
            code: 0,
            msg: 'ok',
            data: encrypteds  // 使用打乱后的数据
            // data: encrypteds  // 使用打乱后的数据
            // meta: {
            //     total: enhancedList.length,
            //     theme: englishTheme,           // 返回转换后的英文主题
            //     moon_phase: englishMoonPhase,  // 返回转换后的英文月相
            //     is_shuffled: true,             // 标识数据已经被打乱
            //     is_streaming: true             // 标识使用了流式生成
            // }
        });

    } catch (e) {
        console.error('肯定语接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }
});
// 数组打乱顺序的工具函数（Fisher-Yates 洗牌算法）
function shuffleArray(array) {
    const shuffled = [...array]; // 创建副本，避免修改原数组
    for (let i = shuffled.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled;
}
// 调用单个模型的辅助函数 - 添加月相参数
async function callSingleModel(modelConfig, theme, language, moonPhase = null) {
    const { model, prompt: realPrompt } = modelConfig;

    console.log(`\n【AI模型】${model}，【本次prompt】\n${realPrompt.slice(0, 50)}${realPrompt.length > 50 ? '...' : ''}`);
    if (moonPhase) {
        console.log(`【月相】${moonPhase}`);
    }

    const aiPayload = {
        model,
        messages: [{
            role: "user",
            content: [{ type: "text", text: realPrompt }]
        }],
        stream: false,
        response_format: { type: "text" },
        temperature: 0.7,
        max_tokens: 1024,
        top_p: 1,
        frequency_penalty: 0,
        top_k: 50
    };

    const t0 = Date.now();
    try {
        const response = await axios.post(
            "https://api-st.siliconflow.cn/v1/chat/completions",
            aiPayload,
            {
                timeout: 15000,
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Bearer sk-tcmxreifxtoyrbjjwcddwlxxqhieysozdluqfnoosmeoeeej`
                }
            }
        );
        const t1 = Date.now();
        console.log(`[计时] 模型: ${model}，本次AI接口耗时: ${t1 - t0}ms`);

        if (!response.data) {
            const error = `[${model}] 接口状态异常(无返回data)`;
            console.error(`[FAIL] ${error}`);
            return { success: false, error, affirmations: [] };
        }

        const result = response.data;
        let text = result?.choices?.[0]?.message?.content || '';

        if (!text.trim()) {
            const error = `[${model}] AI返回内容为空`;
            console.error(`[FAIL] ${error}`);
            return { success: false, error, affirmations: [] };
        }

        // 在这里调用 parseAffirmationsFromText 时传入 moonPhase 参数
        const newAffirmations = parseAffirmationsFromText(text, theme, language, moonPhase);

        if (newAffirmations.length > 0) {
            console.log(`✓ ${model} 成功生成 ${newAffirmations.length} 条肯定语，前两条预览:`);
            for (let i = 0; i < Math.min(newAffirmations.length, 2); i++) {
                const moonInfo = newAffirmations[i].moon_phase ? ` [${newAffirmations[i].moon_phase}]` : '';
                console.log(`  ${i + 1}. ${newAffirmations[i].sentence}${moonInfo}`);
            }
            return { success: true, error: null, affirmations: newAffirmations };
        } else {
            const error = `[${model}] AI返回格式解析失败，原始内容：${text.substring(0, 100)}...`;
            console.error(`[FAIL] ${error}`);
            return { success: false, error, affirmations: [] };
        }

    } catch (e) {
        const t1 = Date.now();
        console.log(`[计时] 模型: ${model}，本次AI接口异常耗时: ${t1 - t0}ms`);

        let error;
        if (e.code === "ECONNABORTED") {
            error = `[${model}] 请求超时`;
            console.error(`[FAIL] ${error}`);
        } else {
            error = `[${model}] 请求异常: ${e.response?.data?.msg || e.message}`;
            console.error(`[FAIL] ${error}`);
        }

        return { success: false, error, affirmations: [] };
    }
}


//肯定语能量识别 - 用户发送自己写的肯定语
router.post('/affirmation/energyRecognition', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    // 请求参数
    let { content, version, pk, user_id, theme, language, moon_phase, sentence, id } = contentx;
    if (!content || !version || !pk) {
        return res.send({ code: 1, msg: '内容、 version、pk必填' });
    }
    // 归一化版本号
    let normVersion;
    try {
        normVersion = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }
    try {
        // 2.1 查询 tarot_feature_config
        const configs = await mysqlQueryTarot(
            `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
            [pk, normVersion, normVersion]
        );
        if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

        let featureList;
        try {
            featureList = JSON.parse(configs[0].feature_list);
        } catch {
            return res.send({ code: 2, msg: '功能配置解析失败' });
        }

        const feature = featureList.find(f => f.featureName === "affirmation_energy_recognition");
        if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

        // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
        console.log(`开始AI流式生成`);
        const { aiResult, lastError, retryCount } = await generateAIStreamContent({
            modelGroups: feature.modelGroups,
            content
        });

        if (!aiResult) {
            // 所有模型都失败（lastError 里一般有具体描述）
            return res.send({
                code: 4,
                msg: `AI流式生成失败。重试次数: ${retryCount || 1}，最后错误: ${lastError || '未知错误'}`
            });
        }
        let universe_reply = parseAIToObject(aiResult).affirmations.metadata.universe_responds
        let fitting = parseAIToObject(aiResult).affirmations.metadata.fitting
        let affirm_count_full = 0 
        if (universe_reply && fitting=='yes') {
            const users = await mysqlQueryTarot('SELECT user_type, invited_count FROM tarot_users WHERE user_id=?', [user_id]);
            if (!users.length) return res.send({ code: 3, msg: '用户不存在' });
            const { user_type, invited_count } = users[0];

            // 判断是新增还是编辑
            if (id) {
                // 编辑
                await mysqlQueryTarot(
                    `UPDATE tarot_user_affirmations
             SET theme=?, language=?, moon_phase=?, sentence=?, universe_reply=?, updated_at=NOW()
             WHERE id=? AND user_id=?`,
                    [
                        theme,
                        language,
                        moon_phase || null,
                        sentence,
                        universe_reply || null,
                        id,
                        user_id
                    ]
                );
            } else {
                // 新增
                if (user_type === 'regular') {
                    // 查已存在数量
                    const countRows = await mysqlQueryTarot('SELECT COUNT(*) as cnt FROM tarot_user_affirmations WHERE user_id=? AND status=1', [user_id]);
                    const affirmCount = countRows[0].cnt;
                    let maxCount = 3;
                    if (parseInt(invited_count, 10) >= 1) maxCount = 6;
                    if (affirmCount >= maxCount) {
                        affirm_count_full = 1;
                    }
                }
                await mysqlQueryTarot(
                    'INSERT INTO tarot_user_affirmations (id, user_id, theme, language, moon_phase, sentence, universe_reply, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,1,NOW(),NOW())',
                    [
                        utils.createId(),
                        user_id,
                        theme,
                        language,
                        moon_phase || null,
                        sentence,
                        universe_reply || null
                    ]
                );
            }
        }
        const encrypteds = encrypt(JSON.stringify(parseAIToObject(aiResult)), config.cryptoKey, config.cryptoIv);

        return res.send({
            code: 0,
            msg: 'ok',
            data: encrypteds, // 这里返回AI原始结果或你按需解析的内容
            affirm_count_full: affirm_count_full
        });
    } catch (e) {
        console.error('肯定语接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }


});

//请求宇宙回应
router.post('/affirmation/universeReply', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    // 请求参数
    let { content, version, pk } = contentx;
    if (!content || !version || !pk) {
        return res.send({ code: 1, msg: '内容、 version、pk必填' });
    }
    // 归一化版本号
    let normVersion;
    try {
        normVersion = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }
    try {
        // 2.1 查询 tarot_feature_config
        const configs = await mysqlQueryTarot(
            `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
            [pk, normVersion, normVersion]
        );
        if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

        let featureList;
        try {
            featureList = JSON.parse(configs[0].feature_list);
        } catch {
            return res.send({ code: 2, msg: '功能配置解析失败' });
        }

        const feature = featureList.find(f => f.featureName === "affirmation_universe_reply");
        if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

        // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
        console.log(`开始AI流式生成`);
        const { aiResult, lastError, retryCount } = await generateAIStreamContent({
            modelGroups: feature.modelGroups,
            content
        });

        if (!aiResult) {
            // 所有模型都失败（lastError 里一般有具体描述）
            return res.send({
                code: 4,
                msg: `AI流式生成失败。重试次数: ${retryCount || 1}，最后错误: ${lastError || '未知错误'}`
            });
        }

        const encrypteds = encrypt(JSON.stringify(parseAIToObject(aiResult)), config.cryptoKey, config.cryptoIv);

        return res.send({
            code: 0,
            msg: 'ok',
            data: encrypteds, // 这里返回AI原始结果或你按需解析的内容
        });
    } catch (e) {
        console.error('肯定语接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }
});

//肯定语继续建议
router.post('/affirmation/recommend', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    // 请求参数
    let { content, version, pk } = contentx;
    if (!content || !version || !pk) {
        return res.send({ code: 1, msg: '内容、 version、pk必填' });
    }
    // 归一化版本号
    let normVersion;
    try {
        normVersion = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }
    try {
        // 2.1 查询 tarot_feature_config
        const configs = await mysqlQueryTarot(
            `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
            [pk, normVersion, normVersion]
        );
        if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

        let featureList;
        try {
            featureList = JSON.parse(configs[0].feature_list);
        } catch {
            return res.send({ code: 2, msg: '功能配置解析失败' });
        }

        const feature = featureList.find(f => f.featureName === "affirmation_high_freq_recommend");
        if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

        // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
        console.log(`开始AI流式生成`);
        const { aiResult, lastError, retryCount } = await generateAIStreamContent({
            modelGroups: feature.modelGroups,
            content
        });

        if (!aiResult) {
            // 所有模型都失败（lastError 里一般有具体描述）
            return res.send({
                code: 4,
                msg: `AI流式生成失败。重试次数: ${retryCount || 1}，最后错误: ${lastError || '未知错误'}`
            });
        }

        const encrypteds = encrypt(JSON.stringify(parseAIToObject(aiResult)), config.cryptoKey, config.cryptoIv);

        return res.send({
            code: 0,
            msg: 'ok',
            data: encrypteds, // 这里返回AI原始结果或你按需解析的内容
        });
    } catch (e) {
        console.error('肯定语接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }
});

//自己的肯定语-添加-编辑-删除
router.post('/userAffirmation/save', async (req, res) => {
    const { user_id, id, theme, language, moon_phase, sentence, universe_reply, status, action } = req.body;

    try {
        if (action === 'add') {
            if (!theme || !language || !sentence || !universe_reply) {
                return res.send({ code: 1, msg: '主题、语言、肯定语句、宇宙回应必填' });
            }

            // 查询用户身份
            const users = await mysqlQueryTarot('SELECT user_type, invited_count FROM tarot_users WHERE user_id=?', [user_id]);
            if (!users.length) return res.send({ code: 3, msg: '用户不存在' });
            const { user_type, invited_count } = users[0];

            if (user_type === 'regular') {
                // 查已存在数量
                const countRows = await mysqlQueryTarot('SELECT COUNT(*) as cnt FROM tarot_user_affirmations WHERE user_id=? AND status=1', [user_id]);
                const affirmCount = countRows[0].cnt;
                let maxCount = 3;
                if (parseInt(invited_count, 10) >= 1) maxCount = 6;
                if (affirmCount >= maxCount) {
                    return res.send({
                        code: 2,
                        msg: `普通用户最多只能拥有${maxCount}条生效肯定语`
                    });
                }
            }

            // 插入
            await mysqlQueryTarot(
                'INSERT INTO tarot_user_affirmations (id, user_id, theme, language, moon_phase, sentence, universe_reply, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,1,NOW(),NOW())',
                [
                    utils.createId(),
                    user_id,
                    theme,
                    language,
                    moon_phase || null,
                    sentence,
                    universe_reply || null
                ]
            );
            return res.send({ code: 0, msg: 'ok' });

        } else if (action === 'edit') {
            if (!id || !theme || !language || !sentence || !universe_reply) {
                return res.send({ code: 1, msg: 'ID,主题、语言、肯定语句、宇宙回应必填' });
            }
            // 判断是否存在
            const exist = await mysqlQueryTarot('SELECT id FROM tarot_user_affirmations WHERE id=? AND user_id=?', [id, user_id]);
            if (!exist.length) return res.send({ code: 3, msg: '肯定语不存在' });

            await mysqlQueryTarot(
                'UPDATE tarot_user_affirmations SET theme=?, language=?, moon_phase=?, sentence=?, universe_reply=?, status=?, updated_at=NOW() WHERE id=? AND user_id=?',
                [
                    theme,
                    language,
                    moon_phase || null,
                    sentence,
                    universe_reply || null,
                    typeof status === 'undefined' ? 1 : status,
                    id,
                    user_id
                ]
            );
            return res.send({ code: 0, msg: 'ok' });

        } else if (action === 'delete') {
            if (!id) return res.send({ code: 1, msg: '肯定语ID必填' });
            // 判断是否存在
            const exist = await mysqlQueryTarot('SELECT id FROM tarot_user_affirmations WHERE id=? AND user_id=?', [id, user_id]);
            if (!exist.length) return res.send({ code: 3, msg: '肯定语不存在' });

            await mysqlQueryTarot(
                'UPDATE tarot_user_affirmations SET status=0, updated_at=NOW() WHERE id=? AND user_id=?',
                [id, user_id]
            );
            return res.send({ code: 0, msg: 'ok' });

        } else {
            return res.send({ code: 4, msg: '未知操作类型' });
        }
    } catch (e) {
        res.send({ code: 9, msg: e.message });
    }
});


//查询自己肯定语列表
router.post('/userAffirmation/list', async (req, res) => {
    const { user_id } = req.body;
    if (!user_id) return res.send({ code: 1, msg: 'user_id必填' });

    try {
        // 1. 查用户身份和邀请数
        const userRows = await mysqlQueryTarot(
            'SELECT user_type, invited_count FROM tarot_users WHERE user_id=? LIMIT 1', [user_id]
        );
        if (!userRows[0]) {
            return res.send({ code: 2, msg: '用户不存在' });
        }
        const { user_type, invited_count } = userRows[0];

        // 2. 查肯定语（降序排列）
        const affirmations = await mysqlQueryTarot(
            'SELECT * FROM tarot_user_affirmations WHERE user_id=? AND status=1 ORDER BY created_at DESC',
            [user_id]
        );

        // 3. 处理加锁逻辑
        let limit = 0;
        let addLocked = false;
        if (user_type === 'regular') {
            if (Number(invited_count) >= 1) {
                limit = 6;
                addLocked = true;
            } else {
                limit = 3;
                addLocked = true;
            }
        }

        // 非regular都不加锁
        const result = affirmations.map((item, idx) => {
            if (!addLocked) {
                return { ...item, locked: false };
            } else {
                return { ...item, locked: idx >= limit };
            }
        });
        // 获取用户收藏的肯定语ID集合
        const favoriteSet = user_id ? await getUserFavoriteAffirmationIdSet(user_id) : new Set();
        const enhancedList = result.map(item => ({
            ...item,
            is_favorite: favoriteSet.has(item.id) ? 1 : 0 //1已 0未
        }));
        res.send({ code: 0, msg: 'ok', data: enhancedList });
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
});


//收藏肯定语接口
router.post('/affirmation/favorite', async (req, res) => {
    const { user_id, affirmation_id, action } = req.body;
    if (!user_id || !affirmation_id || !action) {
        return res.send({ code: 1, msg: 'user_id、affirmation_id、action 必填' });
    }

    try {
        if (action === 'favorite') {
            // 收藏
            await mysqlQueryTarot(
                'INSERT IGNORE INTO tarot_user_favorites (user_id, affirmation_id, created_at) VALUES (?, ?, NOW())',
                [user_id, affirmation_id]
            );
            return res.send({ code: 0, msg: 'success' });
        } else if (action === 'unfavorite') {
            // 取消收藏
            await mysqlQueryTarot(
                'DELETE FROM tarot_user_favorites WHERE user_id=? AND affirmation_id=?',
                [user_id, affirmation_id]
            );
            return res.send({ code: 0, msg: 'success' });
        } else {
            return res.send({ code: 2, msg: 'action参数无效' });
        }
    } catch (e) {
        return res.send({ code: 9, msg: e.message });
    }
});
//获取收藏列表
router.post('/affirmation/favoriteList', async (req, res) => {
    const { user_id, page = 1, pageSize = 20 } = req.body;
    if (!user_id) {
        return res.send({ code: 1, msg: 'user_id 必填' });
    }

    const offset = (page - 1) * pageSize;
    try {
        // 查总数
        const totalRows = await mysqlQueryTarot(
            'SELECT COUNT(*) as total FROM tarot_user_favorites WHERE user_id=?',
            [user_id]
        );
        const total = totalRows[0].total || 0;

        // 主体数据，连表拿到肯定语内容
        const list = await mysqlQueryTarot(
            `SELECT 
                f.id as favorite_id,
                f.affirmation_id,
                f.created_at, 
                a.theme,
                a.language,
                a.moon_phase,
                a.sentence
             FROM tarot_user_favorites f
             LEFT JOIN tarot_affirmations a ON f.affirmation_id = a.id
             WHERE f.user_id = ?
             ORDER BY f.created_at DESC
             LIMIT ? OFFSET ?`,
            [user_id, Number(pageSize), Number(offset)]
        );
        return res.send({
            code: 0,
            msg: 'ok',
            data: list,
            meta: { page, pageSize, total }
        });
    } catch (e) {
        return res.send({ code: 9, msg: e.message });
    }
});

//每日推荐
router.post('/affirmation/dailyRecommend', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    const { user_id, language, version, pk } = contentx;
    if (!user_id) return res.send({ code: 1, msg: 'user_id必填' });

    try {
        // 1. 用户兴趣标签
        const userRows = await mysqlQueryTarot('SELECT interests FROM tarot_users WHERE user_id=? LIMIT 1', [user_id]);
        let interestsArr = [];
        if (userRows.length && userRows[0].interests) {
            interestsArr = userRows[0].interests.split(',').map(t => t.trim()).filter(Boolean);
        }

        // 2. 收藏肯定语的主题
        const favThemeRows = await mysqlQueryTarot(`
            SELECT DISTINCT a.theme 
            FROM tarot_user_favorites f
            LEFT JOIN tarot_affirmations a ON f.affirmation_id = a.id
            WHERE f.user_id = ?`, [user_id]);
        const favThemes = favThemeRows.map(r => r.theme).filter(Boolean);

        // 3. 用户自定义肯定语的主题
        const selfThemeRows = await mysqlQueryTarot(
            'SELECT DISTINCT theme FROM tarot_user_affirmations WHERE user_id=?', [user_id]);
        const selfThemes = selfThemeRows.map(r => r.theme).filter(Boolean);

        // 4. 合并并去重，再标准化小写，防止主题重复
        const allThemesSet = new Set(
            [...interestsArr, ...favThemes, ...selfThemes]
                .filter(Boolean)
                .map(x => x.trim().toLowerCase())
        );
        if (allThemesSet.size === 0) {
            return res.send({
                code: 0,
                msg: 'ok',
                data: [],
                meta: { total: 0, tags: [] }
            });
        }
        const themeArr = Array.from(allThemesSet);

        // ===> ★ 在这里统一定义参数（主题+语言）★
        const baseParams = [...themeArr, language];
        let randomTheme = themeArr[Math.floor(Math.random() * themeArr.length)];
        let content = `肯定语主题="${randomTheme}",输出语言="${language}"`;
        const englishTheme = convertThemeToEnglish(randomTheme);


        // 5. 查询总数
        const themePlaceholders = themeArr.map(() => '?').join(',');
        const countRes = await mysqlQueryTarot(
            `SELECT COUNT(*) as total FROM tarot_affirmations WHERE theme IN (${themePlaceholders}) AND status=1 AND language = ?`, baseParams
        );
        const total = (countRes[0] && countRes[0].total) || 0;
        const favoriteSet = await getUserFavoriteAffirmationIdSet(user_id);
        if (total < 18) {
            let normVersion;
            try {
                normVersion = normalizeVersion(version);
            } catch (e) {
                return res.send({ code: 1, msg: e.message });
            }
            const needCount = 18;

            // 2.1 查询 tarot_feature_config
            const configs = await mysqlQueryTarot(
                `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
                [pk, normVersion, normVersion]
            );
            if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

            let featureList;
            try {
                featureList = JSON.parse(configs[0].feature_list);
            } catch {
                return res.send({ code: 2, msg: '功能配置解析失败' });
            }

            const feature = featureList.find(f => f.featureName === "affirmation_generation");
            if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

            // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
            console.log(`开始AI流式生成，需要补充 ${needCount} 条肯定语`);
            const { affirmations: aiAffirmList, lastError, retryCount } = await generateAffirmationsWithAI(
                feature, randomTheme, language, needCount, null // 传入原始中文参数
            );

            console.log(`AI流式生成完成，共重试 ${retryCount} 次，生成 ${aiAffirmList.length} 条`);
            let affirmations = []
            // 2.3 保存AI生成的肯定语到数据库（已经是英文格式）
            if (aiAffirmList.length > 0) {
                for (const a of aiAffirmList) {
                    try {
                        await mysqlQueryTarot(
                            `INSERT INTO tarot_affirmations (id, theme, language, moon_phase, sentence, status, created_at)
                             VALUES (?, ?, ?, ?, ?, 1, NOW())`,
                            [a.id, englishTheme, language, a.moon_phase, a.sentence]
                        );
                        console.log(`✓ 入库成功: 主题=${englishTheme}, 月相=${a.moon_phase}, 句子=${a.sentence.substring(0, 30)}...`);
                    } catch (insertError) {
                        console.error(`✗ 插入肯定语失败: ${insertError.message}`, a);
                        // 继续插入其他数据，不因个别失败而中断
                    }
                }

                // 2.4 重新查询，获取完整列表
                affirmations = await mysqlQueryTarot(`SELECT id, theme, language, moon_phase, sentence FROM tarot_affirmations 
                        WHERE theme=? AND language=? AND status=1 
                        ORDER BY RAND() LIMIT ?`, [englishTheme, language, 18]);
                console.log(`重新查询后共有 ${affirmations.length} 条肯定语`);
            }

            // 2.5 如果仍然没有足够的数据
            if (affirmations.length === 0) {
                return res.send({
                    code: 4,
                    msg: `未能获取任何肯定语，AI流式生成失败。重试次数: ${retryCount}，最后错误: ${lastError || '未知错误'}`
                });
            }

            // 记录警告日志（数量不足但有部分数据）
            if (affirmations.length < MAX_RANDOM) {
                console.warn(`肯定语数量不足，期望 ${MAX_RANDOM} 条，实际 ${affirmations.length} 条。AI重试 ${retryCount} 次，最后错误: ${lastError}`);
            }
            const shuffledAffirmations = shuffleArray(affirmations);

            // 获取用户收藏的肯定语ID集合
            const favoriteSet = user_id ? await getUserFavoriteAffirmationIdSet(user_id) : new Set();
            const enhancedList = shuffledAffirmations.map(item => ({
                ...item,
                is_favorite: favoriteSet.has(item.id) ? 1 : 0 //1已 0未
            }));

            const encrypteds = encrypt(JSON.stringify(enhancedList), config.cryptoKey, config.cryptoIv);

            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds  // 使用打乱后的数据
                // data: encrypteds  // 使用打乱后的数据
                // meta: {
                //     total: enhancedList.length,
                //     theme: englishTheme,           // 返回转换后的英文主题
                //     moon_phase: englishMoonPhase,  // 返回转换后的英文月相
                //     is_shuffled: true,             // 标识数据已经被打乱
                //     is_streaming: true             // 标识使用了流式生成
                // }
            });
            //     console.log('走1')
            //     // 直接全部返回
            //     const rows = await mysqlQueryTarot(
            //         `SELECT id, theme, language, moon_phase, sentence 
            // FROM tarot_affirmations 
            // WHERE theme IN (${themePlaceholders}) AND status=1 AND language = ?`,
            //         baseParams
            //     );
            //     // 标记是否为已收藏
            //     const enhancedRows = rows.map(item => ({
            //         ...item,
            //         is_favorite: favoriteSet.has(item.id) ? 1 : 0
            //     }));

            //     return res.send({
            //         code: 0,
            //         msg: 'ok',
            //         data: enhancedRows
            //     });
        } else {

            // 6. 大量时先查出所有id，再js取18个unique随机，再查明细
            const idRows = await mysqlQueryTarot(
                `SELECT id FROM tarot_affirmations WHERE theme IN (${themePlaceholders}) AND status=1 AND language = ?`, baseParams
            );
            const ids = idRows.map(r => r.id);
            // 随机选18个
            function getNRandomUnique(arr, n) {
                const total = arr.length;
                if (n >= total) return arr.slice();
                const set = new Set();
                while (set.size < n) {
                    set.add(arr[Math.floor(Math.random() * total)]);
                }
                return Array.from(set);
            }
            const pickedIds = getNRandomUnique(ids, 18);

            // 查询内容明细
            const details = pickedIds.length
                ? await mysqlQueryTarot(
                    `SELECT id, theme, language, moon_phase, sentence 
            FROM tarot_affirmations 
            WHERE id IN (${pickedIds.map(() => '?').join(',')})`,
                    pickedIds
                )
                : [];
            // 增加 is_favorite 字段
            const enhancedRows = details.map(item => ({
                ...item,
                is_favorite: favoriteSet.has(item.id) ? 1 : 0
            }));
            const encrypteds = encrypt(JSON.stringify(enhancedRows), config.cryptoKey, config.cryptoIv);

            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds
            });
        }
    } catch (e) {
        console.error('每日推荐接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }
});

//肯定语视觉版
//获取分类列表
router.post('/visualCollection/list', async (req, res) => {
    const { page = 1, pageSize = 200 } = req.body;
    try {
        const [list, totalRows] = await Promise.all([
            mysqlQueryTarot(`SELECT * FROM tarot_visual_collection WHERE status=1 ORDER BY sort_order,id LIMIT ?,?`, [(page - 1) * pageSize, pageSize]),
            mysqlQueryTarot(`SELECT COUNT(*) as total FROM tarot_visual_collection WHERE status=1`)
        ]);
        // 格式化更新时间
        const listWithFormatTime = list.map(item => ({
            ...item,
            updated_at: formatDate(item.updated_at),
            created_at: formatDate(item.created_at)
        }));
        res.send({ code: 0, data: { list: listWithFormatTime, total: totalRows[0].total } });
    } catch (e) {
        res.send({ code: 9, msg: e.message });
    }
});
//新增/编辑分类
router.post('/visualCollection/save', async (req, res) => {
    let {
        id,
        name,
        color,
        opacity,
        blur,
        min_version = '',
        max_version = '',
        sort_order = 0,
        status = 1,
        langs // 新增的多语言参数
    } = req.body;

    // 1. 版本号规范化
    let min_version_norm = '', max_version_norm = '';
    try {
        min_version_norm = normalizeVersion(min_version);
        max_version_norm = normalizeVersion(max_version);
    } catch (err) {
        return res.status(400).json({ code: 400, msg: err.message });
    }

    // 2. langs 处理：保证入库类型为字符串，空时为 '[]'
    let langsStr;
    if (Array.isArray(langs)) {
        langsStr = JSON.stringify(langs);
    } else if (typeof langs === 'string') {
        langsStr = langs || '[]';
    } else {
        langsStr = '[]';
    }

    try {
        if (id) {
            await mysqlQueryTarot(
                `UPDATE tarot_visual_collection SET 
                     name=?, color=?, opacity=?, blur=?, min_version=?, min_version_norm=?, max_version=?, max_version_norm=?,
                     sort_order=?, status=?, langs=?, updated_at=NOW()
                 WHERE id=?`,
                [
                    name, color, opacity, blur,
                    min_version, min_version_norm,
                    max_version, max_version_norm,
                    sort_order, status,
                    langsStr, // 新增
                    id
                ]
            );
        } else {
            await mysqlQueryTarot(
                `INSERT INTO tarot_visual_collection 
                    (name, color, opacity, blur, min_version, min_version_norm, max_version, max_version_norm, sort_order, status, langs)
                 VALUES (?,?,?,?,?,?,?,?,?,?,?)`,
                [
                    name, color, opacity, blur,
                    min_version, min_version_norm,
                    max_version, max_version_norm,
                    sort_order, status,
                    langsStr // 新增
                ]
            );
        }
        res.send({ code: 0, msg: 'ok' });
    } catch (e) {
        res.send({ code: 9, msg: e.message });
    }
});



//删除分类
router.post('/visualCollection/delete', async (req, res) => {
    const { id } = req.body;
    if (!id) return res.send({ code: 1, msg: 'ID不能为空' });
    try {
        await mysqlQueryTarot('DELETE FROM tarot_visual_collection WHERE id=?', [id]);
        res.send({ code: 0, msg: 'ok' });
    } catch (e) {
        res.send({ code: 9, msg: e.message });
    }
});
//获取素材列表（分页、按分类查）
router.post('/visualMaterial/list', async (req, res) => {
    const { version, visual_collection_id, page = 1, pageSize = 20 } = req.body;
    let wheres = [`status=1`];
    let params = [];
    if (visual_collection_id) { wheres.push('visual_collection_id=?'); params.push(visual_collection_id); }
    // 可以加其它筛选参数，如 version
    let whereStr = wheres.length ? 'WHERE ' + wheres.join(' AND ') : '';
    try {
        const [list, totalRows] = await Promise.all([
            mysqlQueryTarot(
                `SELECT * FROM tarot_visual_material_config ${whereStr} ORDER BY sort_order,id DESC LIMIT ?,?`,
                [...params, (page - 1) * pageSize, pageSize]),
            mysqlQueryTarot(
                `SELECT COUNT(*) as total FROM tarot_visual_material_config ${whereStr}`,
                params)
        ]);
        // 格式化更新时间
        const listWithFormatTime = list.map(item => ({
            ...item,
            updated_at: formatDate(item.updated_at),
            created_at: formatDate(item.created_at)
        }));
        res.send({ code: 0, data: { list: listWithFormatTime, total: totalRows[0].total } });
    } catch (e) {
        res.send({ code: 9, msg: e.message });
    }
});
//新增/编辑素材
router.post('/visualMaterial/save', async (req, res) => {
    let {
        id, min_version = '', max_version = '',
        theme_tags, main_text_material, extra_text_materials, visual_collection_id, visual_collection_name,
        is_subscription_required = 0, thumbnail_url, original_image_url, video_url,
        sort_order = 0, status = 1
    } = req.body;
    // 多选转字符串
    if (Array.isArray(theme_tags)) theme_tags = theme_tags.join(',');
    if (Array.isArray(extra_text_materials)) extra_text_materials = extra_text_materials.join(',');

    // 1. 版本号规范化
    let min_version_norm = '', max_version_norm = '';
    try {
        min_version_norm = normalizeVersion(min_version);
        max_version_norm = normalizeVersion(max_version);
    } catch (err) {
        return res.status(400).json({ code: 400, msg: err.message });
    }

    try {
        if (id) {
            await mysqlQueryTarot(
                `UPDATE tarot_visual_material_config SET min_version=?, min_version_norm=?, max_version=?, max_version_norm=?, theme_tags=?, main_text_material=?, extra_text_materials=?,
                visual_collection_id=?,visual_collection_name=?, is_subscription_required=?, thumbnail_url=?, original_image_url=?, video_url=?, sort_order=?, status=?, updated_at=NOW()
                 WHERE id=?`,
                [min_version, min_version_norm, max_version, max_version_norm,
                    theme_tags, main_text_material, extra_text_materials, visual_collection_id, visual_collection_name,
                    is_subscription_required, thumbnail_url, original_image_url, video_url,
                    sort_order, status, id]
            );
        } else {
            await mysqlQueryTarot(
                `INSERT INTO tarot_visual_material_config 
                    (min_version, min_version_norm, max_version, max_version_norm, theme_tags, main_text_material, extra_text_materials, visual_collection_id,visual_collection_name,
                     is_subscription_required, thumbnail_url, original_image_url, video_url, sort_order, status) 
                 VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
                [min_version, min_version_norm, max_version, max_version_norm,
                    theme_tags, main_text_material, extra_text_materials, visual_collection_id, visual_collection_name,
                    is_subscription_required, thumbnail_url, original_image_url, video_url, sort_order, status]
            );
        }
        res.send({ code: 0, msg: 'ok' });
    } catch (e) {
        res.send({ code: 9, msg: e.message });
    }
});

//删除素材
router.post('/visualMaterial/delete', async (req, res) => {
    const { id } = req.body;
    if (!id) return res.send({ code: 1, msg: 'ID不能为空' });
    try {
        await mysqlQueryTarot('DELETE FROM tarot_visual_material_config WHERE id=?', [id]);
        res.send({ code: 0, msg: 'ok' });
    } catch (e) {
        res.send({ code: 9, msg: e.message });
    }
});

//客户端获取视觉素材
router.post('/visualMaterial/clientList', async (req, res) => {
    let { version } = req.body;
    if (!version) return res.send({ code: 1, msg: '请输入版本号' });

    // 版本号规范化
    let versionNorm;
    try {
        versionNorm = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }

    try {
        // 1. 查分类（版本号区间命中）
        const collections = await mysqlQueryTarot(
            `SELECT  * 
             FROM tarot_visual_collection
             WHERE status=1 AND min_version_norm<=? AND max_version_norm>=? ORDER BY sort_order,id`,
            [versionNorm, versionNorm]
        );
        if (!collections.length) {
            return res.send({ code: 0, data: [] });
        }

        // 2. 获取所有视觉集合的 key 和 id
        const collectionIds = collections.map(c => c.id); // id关联，前端常要
        const collectionMap = {};
        collections.forEach(c => { collectionMap[c.id] = c; });

        // 3. 查所有分类下的素材
        let placeholders = collectionIds.map(() => '?').join(',');
        const materials = await mysqlQueryTarot(
            `SELECT *, id as material_id FROM tarot_visual_material_config
             WHERE status=1 
               AND visual_collection_id IN (${placeholders})
               AND min_version_norm<=? AND max_version_norm>=?
             ORDER BY sort_order, id`,
            [...collectionIds, versionNorm, versionNorm]
        );

        // 4. 分类下素材分组
        const groupMap = {};
        collections.forEach(col => {
            groupMap[col.id] = {
                id: col.id,
                name: col.name,
                langs: JSON.parse(col.langs),
                color: col.color,
                opacity: col.opacity,
                blur: col.blur,
                sort_order: col.sort_order,
                materials: []
            }
        });
        // 分素材进各自分类
        materials.forEach(mat => {
            if (groupMap[mat.visual_collection_id]) {
                groupMap[mat.visual_collection_id].materials.push(mat);
            }
        });

        // 5. 输出有内容的分组（或全返回，视你业务）
        const result = Object.values(groupMap)
            // .filter(g => g.materials.length) // 如果只要有内容的分组
            .sort((a, b) => a.sort_order - b.sort_order);

        res.send({ code: 0, data: result });
    } catch (e) {
        console.error('客户端素材获取错误:', e);
        res.send({ code: 9, msg: e.message });
    }
});


//月相指引
router.post('/moonPhaseGuidance', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    // 请求参数
    let { content, version, pk, date, user_id } = contentx;
    if (!content || !version || !pk || !date || !user_id) {
        return res.send({ code: 1, msg: '内容、 version、date、user_id、pk必填' });
    }
    // 检查当天是否已有AI数据
    try {
        const existRows = await mysqlQueryTarot(
            'SELECT ai_result_json FROM tarot_moon_phase_guidance_user_log WHERE user_id = ? AND day_date = ? LIMIT 1',
            [user_id, date]
        );
        if (existRows.length) {
            const encrypteds = encrypt(JSON.stringify(existRows[0].ai_result_json), config.cryptoKey, config.cryptoIv);
            // 直接返回历史AI数据
            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds,
                date: date
            });
        }
    } catch (e) {
        // 查询异常继续，不影响AI调用
        console.error('查询tarot_moon_phase_guidance_user_log异常', e);
    }


    // 归一化版本号
    let normVersion;
    try {
        normVersion = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }
    try {
        // 2.1 查询 tarot_feature_config
        const configs = await mysqlQueryTarot(
            `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
            [pk, normVersion, normVersion]
        );
        if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

        let featureList;
        try {
            featureList = JSON.parse(configs[0].feature_list);
        } catch {
            return res.send({ code: 2, msg: '功能配置解析失败' });
        }

        const feature = featureList.find(f => f.featureName === "moon_phase_guidance");
        if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

        // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
        console.log(`开始AI流式生成`);
        const { aiResult, lastError, retryCount } = await generateAIStreamContent({
            modelGroups: feature.modelGroups,
            content
        });

        if (!aiResult) {
            // 所有模型都失败（lastError 里一般有具体描述）
            return res.send({
                code: 4,
                msg: `AI流式生成失败。重试次数: ${retryCount || 1}，最后错误: ${lastError || '未知错误'}`
            });
        }

        let dataObj;
        try {
            dataObj = parseAIToObject(aiResult);
        } catch {
            dataObj = null;
        }
        if (typeof dataObj === 'object' && dataObj) {
            // JSON结构正确才入库
            try {
                await mysqlQueryTarot(
                    `INSERT INTO tarot_moon_phase_guidance_user_log (user_id, day_date, ai_result_json) VALUES (?, ?, ?)
                     ON DUPLICATE KEY UPDATE ai_result_json=VALUES(ai_result_json), created_at=NOW()`,
                    [user_id, date, JSON.stringify(dataObj)]
                );
            } catch (e) {
                // 入库失败允许正常返回内容，但建议记录日志
                console.error('tarot_moon_phase_guidance_user_log入库异常', e);
            }
            const encrypteds = encrypt(JSON.stringify(dataObj), config.cryptoKey, config.cryptoIv);
            // 按你需求，data外层必须是 { moon_guidance:{} } 结构
            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds,
                date: date

            });
        }

        const encrypteds = encrypt(JSON.stringify(parseAIToObject(aiResult)), config.cryptoKey, config.cryptoIv);

        // 只要不是合法JSON，不能入库但可返回原始AI内容
        return res.send({
            code: 0,
            msg: 'ok',
            data: encrypteds,
            date: date

        });
    } catch (e) {
        console.error('肯定语接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }
});
// 假定你的 mysqlQueryTarot 已经实现

router.post('/universe_letter_theme/log/check', async (req, res) => {
    const { user_id, date } = req.body;
    if (!user_id || !date) {
        return res.send({ code: 2, msg: '参数缺失' });
    }

    try {
        const sql = `
            SELECT *
            FROM tarot_universe_letter_theme_user_log
            WHERE user_id = ? AND day_date = ?
            LIMIT 1
        `;
        const results = await mysqlQueryTarot(sql, [user_id, date]);
        if (results.length > 0) {
            res.send({ code: 0, msg: 'ok', data: results[0] }); // 直接返回整条数据
        } else {
            res.send({ code: 1, msg: '无记录' });
        }
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
});

//宇宙来信-主题
router.post('/universeLetter/byTheme', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    // 请求参数
    let { content, version, pk, date, user_id, card_name, card_position } = contentx;
    if (!content || !version || !pk || !date || !user_id) {
        return res.send({ code: 1, msg: '内容、 version、date、user_id、pk必填' });
    }
    // 1. 总是优先查库（支持查历史）
    let existRows = [];
    try {
        existRows = await mysqlQueryTarot(
            'SELECT ai_result_json FROM tarot_universe_letter_theme_user_log WHERE user_id = ? AND day_date = ? LIMIT 1',
            [user_id, date]
        );
        if (existRows.length) {
            const encrypteds = encrypt(JSON.stringify(existRows[0].ai_result_json), config.cryptoKey, config.cryptoIv);

            // 查到历史直接返回
            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds
            });
        }
    } catch (e) {
        console.error('查历史异常', e);
        // 这里不 return，让后续走生成流程
    }
    // // 查询用户类型
    // const users = await mysqlQueryTarot('SELECT user_type FROM tarot_users WHERE user_id=?', [user_id]);
    // let user_type = 'regular';
    // if (users.length) {
    //     user_type = users[0].user_type;
    // }
    // // 2. 本地日期(date)与服务器当前允许日期判定
    // // 获取服务器的今天与明天
    // const now = new Date();
    // const todayStr = now.toISOString().slice(0, 10); // '2025-09-09'
    // const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
    // const tomorrowStr = tomorrow.toISOString().slice(0, 10);

    // // 权限判断
    // if (user_type === 'regular') {
    //     // 普通用户只允许抽今天
    //     if (date !== todayStr) {
    //         return res.send({ code: 7, msg: '普通用户只允许抽今天的牌，成为会员可抽明天的牌' });
    //     }
    // } else {
    //     // 会员可抽今明两天
    //     if (date !== todayStr && date !== tomorrowStr) {
    //         return res.send({ code: 6, msg: '会员只能抽今天或明天的牌，不能抽过去的牌' });
    //     }
    // }
    // 归一化版本号
    let normVersion;
    try {
        normVersion = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }
    try {
        // 2.1 查询 tarot_feature_config
        const configs = await mysqlQueryTarot(
            `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
            [pk, normVersion, normVersion]
        );
        if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

        let featureList;
        try {
            featureList = JSON.parse(configs[0].feature_list);
        } catch {
            return res.send({ code: 2, msg: '功能配置解析失败' });
        }

        const feature = featureList.find(f => f.featureName === "tarot_interpretation_theme");
        if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

        // 在AI调用前取用户主题相关
        const userRows = await mysqlQueryTarot('SELECT interests FROM tarot_users WHERE user_id=? LIMIT 1', [user_id]);
        let interestsArr = [];
        if (userRows.length && userRows[0].interests) {
            interestsArr = userRows[0].interests.split(',').map(t => t.trim()).filter(Boolean);
        }

        // 收藏肯定语的主题
        const favThemeRows = await mysqlQueryTarot(`
    SELECT DISTINCT a.theme 
    FROM tarot_user_favorites f
    LEFT JOIN tarot_affirmations a ON f.affirmation_id = a.id
    WHERE f.user_id = ?`, [user_id]);
        const favThemes = favThemeRows.map(r => r.theme).filter(Boolean);

        // 自定义肯定语的主题
        const selfThemeRows = await mysqlQueryTarot(
            'SELECT DISTINCT theme FROM tarot_user_affirmations WHERE user_id=?', [user_id]);
        const selfThemes = selfThemeRows.map(r => r.theme).filter(Boolean);

        // 合并去重并规范小写
        const allThemesArr = [...interestsArr, ...favThemes, ...selfThemes]
            .filter(Boolean)
            .map(x => x.trim().toLowerCase());
        const allThemesSet = Array.from(new Set(allThemesArr));

        // 快速构造英文到中文的映射
        const mappingReverse = {};
        for (const [cn, en] of Object.entries(mapping)) {
            mappingReverse[en] = cn;
        }

        // 随机英文主题
        let selectedTheme = '';
        if (allThemesSet.length > 0) {
            const randomIdx = Math.floor(Math.random() * allThemesSet.length);
            selectedTheme = allThemesSet[randomIdx]; // 这里全是英文
        }

        // 反查中文名
        const themeCn = mappingReverse[selectedTheme] || selectedTheme;

        // content 拼接
        if (selectedTheme) {
            content = content + `\n主题=${themeCn}`;
        }
        // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
        console.log(`开始AI流式生成`);
        const { aiResult, lastError, retryCount } = await generateAIStreamContent({
            modelGroups: feature.modelGroups,
            content
        });

        if (!aiResult) {
            // 所有模型都失败（lastError 里一般有具体描述）
            return res.send({
                code: 4,
                msg: `AI流式生成失败。重试次数: ${retryCount || 1}，最后错误: ${lastError || '未知错误'}`
            });
        }

        let dataObj;
        try {
            dataObj = parseAIToObject(aiResult);
        } catch {
            dataObj = null;
        }
        if (typeof dataObj === 'object' && dataObj) {
            // JSON结构正确才入库
            try {
                await mysqlQueryTarot(
                    `INSERT INTO tarot_universe_letter_theme_user_log (user_id, day_date, ai_result_json, card_name, card_position) VALUES (?, ?, ?, ?, ?)
                     ON DUPLICATE KEY UPDATE ai_result_json=VALUES(ai_result_json), created_at=NOW()`,
                    [user_id, date, JSON.stringify(dataObj), card_name, card_position]
                );
            } catch (e) {
                // 入库失败允许正常返回内容，但建议记录日志
                console.error('tarot_universe_letter_theme_user_log入库异常', e);
            }
            const encrypteds = encrypt(JSON.stringify(dataObj), config.cryptoKey, config.cryptoIv);

            // 按你需求，data外层必须是 { moon_guidance:{} } 结构
            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds
            });
        }
        const encrypteds = encrypt(JSON.stringify(parseAIToObject(aiResult)), config.cryptoKey, config.cryptoIv);

        // 只要不是合法JSON，不能入库但可返回原始AI内容
        return res.send({
            code: 0,
            msg: 'ok',
            data: encrypteds
        });
    } catch (e) {
        console.error('肯定语接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }
});
//宇宙来信-月相
router.post('/universeLetter/byMoonPhase', async (req, res) => {
    let parseData = req.body.query;
    let contentx = JSON.parse(decrypt(parseData, config.cryptoKey, config.cryptoIv));
    const chinaTime = new Date();
    if (contentx.code !== 'OZqcJQLBUyevTh') return res.send({ code: 1, msg: 'code错误' });
    // 请求参数
    let { content, version, pk, date, user_id, card_name, card_position } = contentx;
    if (!content || !version || !pk || !date || !user_id) {
        return res.send({ code: 1, msg: '内容、 version、date、user_id、pk必填' });
    }

    // 1. 总是优先查库（支持查历史）
    let existRows = [];
    try {
        existRows = await mysqlQueryTarot(
            'SELECT ai_result_json FROM tarot_universe_letter_theme_user_log WHERE user_id = ? AND day_date = ? LIMIT 1',
            [user_id, date]
        );
        if (existRows.length) {
            const encrypteds = encrypt(JSON.stringify(existRows[0].ai_result_json), config.cryptoKey, config.cryptoIv);

            // 查到历史直接返回
            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds
            });
        }
    } catch (e) {
        console.error('查历史异常', e);
        // 这里不 return，让后续走生成流程
    }
    // 查询用户类型
    // const users = await mysqlQueryTarot('SELECT user_type FROM tarot_users WHERE user_id=?', [user_id]);
    // let user_type = 'regular';
    // if (users.length) {
    //     user_type = users[0].user_type;
    // }
    // // 2. 本地日期(date)与服务器当前允许日期判定
    // // 获取服务器的今天与明天
    // const now = new Date();
    // const todayStr = now.toISOString().slice(0, 10); // '2025-09-09'
    // const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
    // const tomorrowStr = tomorrow.toISOString().slice(0, 10);

    // // 权限判断
    // if (user_type === 'regular') {
    //     // 普通用户只允许抽今天
    //     if (date !== todayStr) {
    //         return res.send({ code: 7, msg: '普通用户只允许抽今天的牌，成为会员可抽明天的牌' });
    //     }
    // } else {
    //     // 会员可抽今明两天
    //     if (date !== todayStr && date !== tomorrowStr) {
    //         return res.send({ code: 6, msg: '会员只能抽今天或明天的牌，不能抽过去的牌' });
    //     }
    // }



    // 归一化版本号
    let normVersion;
    try {
        normVersion = normalizeVersion(version);
    } catch (e) {
        return res.send({ code: 1, msg: e.message });
    }
    try {
        // 2.1 查询 tarot_feature_config
        const configs = await mysqlQueryTarot(
            `SELECT feature_list FROM tarot_feature_config WHERE status=1 AND pk=? AND min_version_norm<=? AND max_version_norm>=? LIMIT 1`,
            [pk, normVersion, normVersion]
        );
        if (!configs.length) return res.send({ code: 2, msg: '功能配置未找到' });

        let featureList;
        try {
            featureList = JSON.parse(configs[0].feature_list);
        } catch {
            return res.send({ code: 2, msg: '功能配置解析失败' });
        }

        const feature = featureList.find(f => f.featureName === "tarot_interpretation_moon_phase");
        if (!feature || !feature.modelGroups) return res.send({ code: 2, msg: 'AI模型配置缺失' });

        // 2.2 AI流式生成（传入原始中文主题给AI，但存储时用英文）
        console.log(`开始AI流式生成`);
        const { aiResult, lastError, retryCount } = await generateAIStreamContent({
            modelGroups: feature.modelGroups,
            content
        });

        if (!aiResult) {
            // 所有模型都失败（lastError 里一般有具体描述）
            return res.send({
                code: 4,
                msg: `AI流式生成失败。重试次数: ${retryCount || 1}，最后错误: ${lastError || '未知错误'}`
            });
        }

        let dataObj;
        try {
            dataObj = parseAIToObject(aiResult);
        } catch {
            dataObj = null;
        }
        if (typeof dataObj === 'object' && dataObj) {
            // JSON结构正确才入库
            try {
                await mysqlQueryTarot(
                    `INSERT INTO tarot_universe_letter_theme_user_log (user_id, day_date, ai_result_json, card_name, card_position) VALUES (?, ?, ?, ?, ?)
                     ON DUPLICATE KEY UPDATE ai_result_json=VALUES(ai_result_json), created_at=NOW()`,
                    [user_id, date, JSON.stringify(dataObj), card_name, card_position]
                );
            } catch (e) {
                // 入库失败允许正常返回内容，但建议记录日志
                console.error('tarot_universe_letter_theme_user_log入库异常', e);
            }
            const encrypteds = encrypt(JSON.stringify(dataObj), config.cryptoKey, config.cryptoIv);

            // 按你需求，data外层必须是 { moon_guidance:{} } 结构
            return res.send({
                code: 0,
                msg: 'ok',
                data: encrypteds
            });
        }
        const encrypteds = encrypt(JSON.stringify(parseAIToObject(aiResult)), config.cryptoKey, config.cryptoIv);

        // 只要不是合法JSON，不能入库但可返回原始AI内容
        return res.send({
            code: 0,
            msg: 'ok',
            data: encrypteds
        });
    } catch (e) {
        console.error('肯定语接口异常:', e);
        return res.send({ code: 9, msg: e.message });
    }
});



//后台用
// router.js 片段

router.post('/tarot-users/list', async (req, res) => {
    let {
        page = 1,
        size = 10,
        username = "",
        user_type = "",
        country = "",
        city = "",
        gender = "",
        registration_time_start = "",
        registration_time_end = ""
    } = req.body;
    page = parseInt(page, 10);
    size = parseInt(size, 10);

    // 组装where条件
    let wheres = [], params = [];
    if (username) {
        wheres.push("username = ?");
        params.push(username);
    }
    if (user_type) {
        wheres.push("user_type = ?");
        params.push(user_type);
    }
    if (country) {
        wheres.push("country = ?");
        params.push(country);
    }
    if (city) {
        wheres.push("city = ?");
        params.push(city);
    }
    if (gender !== "" && gender !== null && gender !== undefined) {
        wheres.push("gender = ?");
        params.push(Number(gender));
    }
    if (registration_time_start) {
        wheres.push("registration_time >= ?");
        params.push(registration_time_start + ' 00:00:00');
    }
    if (registration_time_end) {
        wheres.push("registration_time <= ?");
        params.push(registration_time_end + ' 23:59:59');
    }
    let whereStr = wheres.length ? 'WHERE ' + wheres.join(' AND ') : '';
    let offset = (page - 1) * size;

    // 主查询，新增两个子查询统计字段
    let sql = `
        SELECT u.*,
            (SELECT COUNT(1) FROM tarot_user_affirmations ua WHERE ua.user_id = u.user_id AND ua.status=1) AS userAffirmationsCount,
            (SELECT COUNT(1) FROM tarot_user_favorites uf WHERE uf.user_id = u.user_id) AS favoriteAffirmationsCount
        FROM tarot_users u
        ${whereStr}
        ORDER BY registration_time DESC
        LIMIT ?, ?
    `;
    params.push(offset);
    params.push(size);

    // 总数查询
    let countSql = `SELECT COUNT(1) AS total FROM tarot_users u ${whereStr}`;

    try {
        // 查询数据和总数
        let [list, totalResult] = await Promise.all([
            mysqlQueryTarot(sql, params),
            mysqlQueryTarot(countSql, params.slice(0, -2)), // 统计不用limit/offset
        ]);
        // 格式化时间
        const listWithFormatTime = list.map(item => ({
            ...item,
            updated_at: formatDate(item.updated_at),
            registration_time: formatDate(item.registration_time),
            last_login_time: formatDate(item.last_login_time),
            subscription_start_date: formatDate(item.subscription_start_date),
            subscription_expiry_date: formatDate(item.subscription_expiry_date),
            invitation_time: formatDate(item.invitation_time),
            created_at: formatDate(item.created_at)
        }));
        res.send({
            code: 0,
            data: {
                listWithFormatTime,
                total: totalResult[0].total
            }
        });
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
});


router.get('/countries', async (req, res) => {
    try {
        const rows = await mysqlQueryTarot(`SELECT DISTINCT country FROM tarot_users WHERE country IS NOT NULL AND country!=''`);
        const data = rows.map(i => i.country);
        res.send({ code: 0, data });
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
});
router.post('/tarot-users/detail', async (req, res) => {
    const { user_id } = req.body;
    if (!user_id) return res.send({ code: 2, msg: '缺少user_id' });
    try {
        const rows = await mysqlQueryTarot(`SELECT * FROM tarot_users WHERE user_id=?`, [user_id]);
        if (!rows.length) return res.send({ code: 404, msg: '未找到用户' });
        res.send({ code: 0, data: rows[0] });
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
})

router.get('/user-affirmations/list', async (req, res) => {
    const { user_id, page = 1, page_size = 10 } = req.query;
    if (!user_id) return res.send({ code: 2, msg: '用户ID必填' });
    const limit = Number(page_size), offset = (Number(page) - 1) * limit;
    const sql = `SELECT * FROM tarot_user_affirmations WHERE user_id=? AND status=1 ORDER BY created_at DESC LIMIT ? OFFSET ?`;
    const countSql = `SELECT count(1) as total FROM tarot_user_affirmations WHERE user_id=? AND status=1`;
    try {
        let [list1, countRows] = await Promise.all([
            mysqlQueryTarot(sql, [user_id, limit, offset]),
            mysqlQueryTarot(countSql, [user_id])
        ]);
        const list = list1.map(item => ({
            ...item,
            created_at: formatDate(item.created_at),
            updated_at: formatDate(item.updated_at)
        }));
        res.send({ code: 0, data: { list, total: (countRows[0] && countRows[0].total) || 0 } });
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
});
router.get('/user-affirmations/favorites', async (req, res) => {
    const { user_id, page = 1, page_size = 10 } = req.query;
    if (!user_id) return res.send({ code: 2, msg: '用户ID必填' });
    const limit = Number(page_size), offset = (Number(page) - 1) * limit;
    // 只返回有效肯定语
    const sql = `
      SELECT 
        f.id, f.affirmation_id, f.created_at,
        a.theme, a.language, a.moon_phase, a.sentence
      FROM tarot_user_favorites f
      JOIN tarot_affirmations a ON f.affirmation_id = a.id AND a.status=1
      WHERE f.user_id = ?
      ORDER BY f.created_at DESC
      LIMIT ? OFFSET ?
    `;
    const countSql = `
      SELECT COUNT(*) as total
      FROM tarot_user_favorites f
      JOIN tarot_affirmations a ON f.affirmation_id = a.id AND a.status=1
      WHERE f.user_id = ?
    `;
    try {
        let [list1, countRows] = await Promise.all([
            mysqlQueryTarot(sql, [user_id, limit, offset]),
            mysqlQueryTarot(countSql, [user_id])
        ]);
        const list = list1.map(item => ({
            ...item,
            created_at: formatDate(item.created_at)
        }));
        res.send({ code: 0, data: { list, total: (countRows[0] && countRows[0].total) || 0 } });
    } catch (e) {
        res.send({ code: 1, msg: e.message });
    }
});



// 工具函数：格式化为 YYYY-MM-DD HH:mm:ss
function formatDate(datetime) {
    if (!datetime) return '';
    const date = new Date(datetime);
    const pad = n => n < 10 ? '0' + n : n;
    const Y = date.getFullYear();
    const M = pad(date.getMonth() + 1);
    const D = pad(date.getDate());
    const h = pad(date.getHours());
    const m = pad(date.getMinutes());
    const s = pad(date.getSeconds());
    return `${Y}-${M}-${D} ${h}:${m}:${s}`;
}
async function mysqlQueryTarot(sql, arr) {
    return new Promise((resolve, reject) => {
        poolTarot.query(sql, arr, (error, results, fields) => {
            if (error) {
                return reject(error);
            }
            resolve(results);
        });
    });
}
async function mysqlQueryWithConn(sql, arr, conn) {
    return new Promise((resolve, reject) => {
        conn.query(sql, arr, (err, results, fields) => {
            if (err) reject(err);
            else resolve(results);
        });
    });
}
async function mysqlQuery(sql, arr) {
    return new Promise((resolve, reject) => {
        pool.query(sql, arr, (error, results, fields) => {
            if (error) {
                return reject(error);
            }
            resolve(results);
        });
    });
}
// 加密函数
function encrypt(message, key, iv) {
    const keyHex = CryptoJS.enc.Utf8.parse(key);
    const ivHex = CryptoJS.enc.Utf8.parse(iv);
    // 加密模式为CBC，填充模式为PKCS7
    const encrypted = CryptoJS.DES.encrypt(message, keyHex, {
        iv: ivHex,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7,
    });
    return encrypted.toString();
}

// 解密函数
function decrypt(ciphertext, key, iv) {
    const keyHex = CryptoJS.enc.Utf8.parse(key);
    const ivHex = CryptoJS.enc.Utf8.parse(iv); // 使用key的前8个字符作为IV
    // 解密模式为CBC，填充模式为PKCS7
    const decrypted = CryptoJS.DES.decrypt(
        {
            ciphertext: CryptoJS.enc.Base64.parse(ciphertext),
        },
        keyHex,
        {
            iv: ivHex,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7,
        }
    );
    return decrypted.toString(CryptoJS.enc.Utf8);
}
module.exports = router;
